diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07c03228..c469226e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -136,10 +136,57 @@ jobs: - name: Build packages first run: pnpm build + - name: Typecheck all packages + run: pnpm -r typecheck + - name: Typecheck desktop working-directory: apps/desktop run: pnpm typecheck + # ── Tier 2: E2E (Playwright + Electron, xvfb on Linux) ───────────────── + # Starts as continue-on-error: true while we stabilize the suite. + # Flip to required once it's reliably green on develop. + e2e: + needs: setup + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v5 + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Restore node_modules + uses: actions/cache/restore@v4 + with: + path: | + node_modules + apps/*/node_modules + packages/*/node_modules + key: modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install Playwright system deps + working-directory: apps/desktop + run: npx playwright install-deps chromium + + - name: Build desktop bundle + run: pnpm --filter @readied/desktop build + + - name: Run Playwright E2E (xvfb) + working-directory: apps/desktop + run: xvfb-run --auto-servernum pnpm e2e + env: + CI: 'true' + + - name: Upload Playwright report on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: apps/desktop/playwright-report/ + retention-days: 7 + # ── Tier 3: Security audit ───────────────────────── security: needs: setup diff --git a/.gitignore b/.gitignore index 96f7a24c..4b171feb 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,14 @@ npm-debug.log* # Cache .eslintcache +# Vitest coverage reports +coverage/ + +# Playwright artifacts +test-results/ +playwright-report/ +playwright/.cache/ + # Screenshots (root level only) /CleanShot*.png diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100644 index 9ef41ae4..00000000 --- a/.husky/commit-msg +++ /dev/null @@ -1 +0,0 @@ -pnpm commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100644 index cb2c84d5..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -pnpm lint-staged diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100644 index 0db91f05..00000000 --- a/.husky/pre-push +++ /dev/null @@ -1 +0,0 @@ -pnpm typecheck diff --git a/apps/desktop/e2e/README.md b/apps/desktop/e2e/README.md new file mode 100644 index 00000000..64e83a60 --- /dev/null +++ b/apps/desktop/e2e/README.md @@ -0,0 +1,49 @@ +# E2E tests (Playwright + Electron) + +End-to-end tests for the desktop app, driven through Playwright's `_electron` API. Tests launch the **built** Electron bundle in `out/`, so you must run `pnpm build` (or `pnpm dev` for headed iteration) before they pass. + +## Running locally + +```bash +# From repo root +pnpm --filter @readied/desktop build # produces out/main/index.js +pnpm --filter @readied/desktop e2e # headless +pnpm --filter @readied/desktop e2e:headed # opens the window +``` + +First run also downloads Playwright's browser binaries: + +```bash +npx playwright install --with-deps +``` + +(`--with-deps` only matters on Linux, where it installs system libs.) + +## Isolation + +`launchApp()` in `fixtures.ts` creates a fresh temp `userData` dir per test, so: + +- The SQLite DB starts empty every time. +- Settings, license cache, AI keys, etc. don't leak between tests. +- The host's real Readied data is never touched. + +Set `READIED_E2E_KEEP_USERDATA=1` to keep the temp dir on failure for post-mortem inspection. + +## What we test + +| Spec | What it covers | +| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `smoke.spec.ts` | App launches, main window renders, IPC bridge present, no uncaught console errors during initial mount. This is the regression catch for #266 (editor mount crashes producing blank windows). | +| `notes.spec.ts` | Notes IPC contract — create / list / get roundtrip, FTS5 search returns freshly-created notes. We deliberately drive the **preload bridge** (`window.readied.notes.*`) rather than the editor UI; selectors churn but the contract is stable. | + +## What we deliberately don't test (yet) + +- **Editor UI interactions** (typing, formatting, hotkeys). The CodeMirror surface is too prone to flake without per-spec selectors. Worth doing once the editor is split (see PR-G in the audit). +- **AI panel streaming.** Needs a mock provider and is more useful as a vitest test against `@readied/ai-core`. +- **Sync flows.** Need a fake server. + +These will be follow-ups once the basics are stable in CI. + +## CI + +The `e2e` job in `.github/workflows/ci.yml` runs on Linux + xvfb. It starts as `continue-on-error: true` — the goal of this PR is to land the infrastructure, not to gate every PR on E2E green. Once the suite is verified end-to-end on a real CI run, flip the flag off in a follow-up. diff --git a/apps/desktop/e2e/fixtures.ts b/apps/desktop/e2e/fixtures.ts new file mode 100644 index 00000000..21e596bd --- /dev/null +++ b/apps/desktop/e2e/fixtures.ts @@ -0,0 +1,65 @@ +/** + * Shared E2E fixtures for Electron app tests. + * + * `launchApp()` launches a fresh Electron instance with an isolated + * userData directory so tests don't interfere with each other or with + * a developer's local Readied install. Each test should call this in + * its own `beforeEach`. + */ + +import { mkdtemp, rm } from 'fs/promises'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { _electron as electron, type ElectronApplication, type Page } from '@playwright/test'; + +interface LaunchedApp { + app: ElectronApplication; + window: Page; + userDataDir: string; + /** Call in afterEach. */ + cleanup: () => Promise; +} + +/** + * Launches the desktop app and waits for the first window to be ready. + * + * Uses a fresh temp `userData` so the test gets an empty database every + * time. Set READIED_E2E_KEEP_USERDATA=1 to keep the dir on failure for + * post-mortem. + */ +export async function launchApp(): Promise { + const userDataDir = await mkdtemp(join(tmpdir(), 'readied-e2e-')); + + const app = await electron.launch({ + args: [ + '.', + `--user-data-dir=${userDataDir}`, + // Disable updates / external network checks during tests. + '--disable-features=AutoUpdate', + ], + env: { + ...process.env, + NODE_ENV: 'test', + READIED_E2E: '1', + // Pin the data root explicitly so the app uses our temp dir for + // its SQLite database too, not just for Electron's userData. + READIED_DATA_DIR: userDataDir, + }, + }); + + const window = await app.firstWindow(); + // Wait for the renderer to finish initial paint. + await window.waitForLoadState('domcontentloaded'); + + return { + app, + window, + userDataDir, + cleanup: async () => { + await app.close().catch(() => {}); + if (process.env.READIED_E2E_KEEP_USERDATA !== '1') { + await rm(userDataDir, { recursive: true, force: true }).catch(() => {}); + } + }, + }; +} diff --git a/apps/desktop/e2e/notes.spec.ts b/apps/desktop/e2e/notes.spec.ts new file mode 100644 index 00000000..7552ea18 --- /dev/null +++ b/apps/desktop/e2e/notes.spec.ts @@ -0,0 +1,113 @@ +import { test, expect } from '@playwright/test'; +import { launchApp } from './fixtures.js'; + +/** + * Notes CRUD end-to-end. + * + * We exercise the IPC contract directly through the preload bridge + * (`window.readied.notes`) rather than driving the editor UI. This is + * intentional: + * - The UI elements (selectors, labels, hotkeys) churn often. Asserting + * against the IPC surface gives us regression coverage on the + * *contract* that survives renderer refactors. + * - Anything that breaks here also breaks the desktop's renderer code, + * because the renderer uses the same bridge. + */ +test.describe('notes IPC contract', () => { + test('create → list → read roundtrip', async () => { + const { window, cleanup } = await launchApp(); + try { + const noteId = `e2e-${Date.now()}-${Math.floor(Math.random() * 1e6)}`; + const content = '# E2E note\n\nbody from playwright'; + + const createResult = await window.evaluate( + async ([id, body]) => { + const api = ( + window as unknown as { + readied: { + notes: { + create: (input: { + id?: string; + content: string; + notebookId?: string; + }) => Promise; + list: ( + opts?: Record + ) => Promise>; + get: (id: string) => Promise; + }; + }; + } + ).readied; + const created = await api.notes.create({ id, content: body }); + return { created }; + }, + [noteId, content] as const + ); + + expect(createResult.created).toBeTruthy(); + + const list = await window.evaluate( + async () => + ( + window as unknown as { + readied: { + notes: { + list: () => Promise>; + }; + }; + } + ).readied.notes.list(), + undefined + ); + + const ourNote = list.find(n => n.id === noteId); + expect(ourNote, `note ${noteId} missing from list`).toBeDefined(); + expect(ourNote!.content).toContain('body from playwright'); + } finally { + await cleanup(); + } + }); + + test('search returns the freshly-created note via FTS5', async () => { + const { window, cleanup } = await launchApp(); + try { + const marker = `marker_${Date.now()}_unique`; + await window.evaluate( + async ([body]) => { + const api = ( + window as unknown as { + readied: { + notes: { create: (input: { content: string }) => Promise }; + }; + } + ).readied; + await api.notes.create({ content: `# Searchable\n\n${body}` }); + }, + [marker] as const + ); + + const results = await window.evaluate( + async ([q]) => + ( + window as unknown as { + readied: { + notes: { + search: ( + query: string, + limit?: number + ) => Promise>; + }; + }; + } + ).readied.notes.search(q, 10), + [marker] as const + ); + + expect(results.length).toBeGreaterThan(0); + expect(results.some(r => r.content.includes(marker))).toBe(true); + } finally { + await cleanup(); + } + }); +}); diff --git a/apps/desktop/e2e/smoke.spec.ts b/apps/desktop/e2e/smoke.spec.ts new file mode 100644 index 00000000..85bf76b2 --- /dev/null +++ b/apps/desktop/e2e/smoke.spec.ts @@ -0,0 +1,58 @@ +import { test, expect } from '@playwright/test'; +import { launchApp } from './fixtures.js'; + +test.describe('app launch (smoke)', () => { + test('launches and shows the main window', async () => { + const { app, window, cleanup } = await launchApp(); + try { + // Title is "Readied" in production. Allow any non-empty title in case + // dev/test envs use a different one. + const title = await window.title(); + expect(title.length).toBeGreaterThan(0); + + // First window must render *something* — a element with non-zero + // size is a low bar that catches the regression class from PR #266 + // (editor mount crashes that produced a blank window). + const bodyBox = await window.locator('body').boundingBox(); + expect(bodyBox).not.toBeNull(); + expect(bodyBox!.width).toBeGreaterThan(0); + expect(bodyBox!.height).toBeGreaterThan(0); + + // Sanity: the app exposed its IPC bridge. + const hasBridge = await window.evaluate( + () => typeof (window as unknown as { readied?: unknown }).readied !== 'undefined' + ); + expect(hasBridge).toBe(true); + + expect(app.windows().length).toBeGreaterThanOrEqual(1); + } finally { + await cleanup(); + } + }); + + test('console does not log uncaught errors during initial render', async () => { + const { window, cleanup } = await launchApp(); + const consoleErrors: string[] = []; + window.on('console', msg => { + if (msg.type() === 'error') consoleErrors.push(msg.text()); + }); + window.on('pageerror', err => consoleErrors.push(`pageerror: ${err.message}`)); + + try { + // Give the renderer 3s to throw any early errors during mount. + await window.waitForTimeout(3000); + + // Known non-fatal noise that the app emits in test/dev environments. + // Strip these out before asserting "no errors". + const ignored = [ + /\[Sentry\]/, // "No DSN configured" — expected without VITE_SENTRY_DSN + /Failed to load resource: net::ERR_/, // network during dev sometimes + ]; + const real = consoleErrors.filter(line => !ignored.some(re => re.test(line))); + + expect(real, real.join('\n')).toEqual([]); + } finally { + await cleanup(); + } + }); +}); diff --git a/apps/desktop/e2e/tsconfig.json b/apps/desktop/e2e/tsconfig.json new file mode 100644 index 00000000..1eb7b599 --- /dev/null +++ b/apps/desktop/e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "bundler", + "rootDir": ".", + "noEmit": true + }, + "include": ["**/*.ts"] +} diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 07e4f704..3ca3601f 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -18,43 +18,43 @@ "typecheck:main": "tsc --noEmit -p src/main/tsconfig.json", "typecheck:preload": "tsc --noEmit -p src/preload/tsconfig.json", "typecheck:renderer": "tsc --noEmit -p src/renderer/tsconfig.json", - "typecheck": "pnpm run typecheck:main && pnpm run typecheck:preload && pnpm run typecheck:renderer", + "typecheck:e2e": "tsc --noEmit -p e2e/tsconfig.json", + "typecheck": "pnpm run typecheck:main && pnpm run typecheck:preload && pnpm run typecheck:renderer && pnpm run typecheck:e2e", "pack": "electron-builder --dir", "dist": "electron-builder", "dist:mac": "electron-builder --mac", "dist:win": "electron-builder --win", "dist:linux": "electron-builder --linux", "test": "vitest run", - "test:watch": "vitest" + "test:watch": "vitest", + "e2e": "playwright test", + "e2e:headed": "playwright test --headed" }, "dependencies": { - "@codemirror/autocomplete": "^6.20.1", + "@codemirror/autocomplete": "^6.20.3", "@codemirror/commands": "^6.10.3", "@codemirror/lang-markdown": "^6.5.0", "@codemirror/language": "^6.12.3", "@codemirror/language-data": "^6.5.2", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.41.1", + "@codemirror/view": "^6.43.0", "@lezer/highlight": "^1.2.3", - "@sentry/electron": "^7.11.0", - "@tanstack/react-query": "^5.100.1", - "better-sqlite3": "^12.9.0", + "@sentry/electron": "^7.13.0", + "@tanstack/react-query": "^5.101.0", + "better-sqlite3": "^12.10.0", "cross-fetch": "^4.1.0", "diff": "^9.0.0", - "electron-updater": "^6.8.3", - "highlight.js": "^11.11.1", - "isomorphic-git": "^1.37.5", - "lucide-react": "^1.8.0", + "electron-updater": "^6.8.9", + "isomorphic-git": "^1.38.4", + "lucide-react": "^1.17.0", "pino": "^10.3.1", - "pino-roll": "^4.0.0", "react-markdown": "^10.1.0", - "react-resizable-panels": "^4.10.0", "rehype-highlight": "^7.0.2", "remark-gfm": "^4.0.1", "turndown": "^7.2.4", "turndown-plugin-gfm": "^1.0.2", - "unist-util-visit": "^5.1.0", - "zustand": "^5.0.12" + "zod": "^4.4.3", + "zustand": "^5.0.14" }, "devDependencies": { "@readied/ai-core": "workspace:*", @@ -70,23 +70,22 @@ "@readied/tasks": "workspace:*", "@readied/wikilinks": "workspace:*", "@types/better-sqlite3": "^7.6.12", - "@types/mdast": "^4.0.4", - "@types/react": "^19.2.14", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@types/turndown": "^5.0.6", - "@vitejs/plugin-react": "^6.0.1", - "electron": "^41.3.0", - "electron-builder": "^26.8.1", + "@playwright/test": "^1.49.1", + "@vitejs/plugin-react": "^6.0.2", + "electron": "^42.3.3", + "electron-builder": "^26.15.2", "electron-devtools-installer": "^4.0.0", "electron-vite": "^5.0.0", - "pino-pretty": "^13.1.3", - "react": "^19.2.5", - "react-dom": "^19.2.5", + "react": "^19.2.7", + "react-dom": "^19.2.7", "react-force-graph-2d": "^1.29.1", "rehype-raw": "^7.0.0", - "typescript": "^5.7.2", - "vite": "^8.0.10", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vite": "^8.0.16", + "vitest": "^4.1.8" }, "build": { "appId": "app.readied.desktop", diff --git a/apps/desktop/playwright.config.ts b/apps/desktop/playwright.config.ts new file mode 100644 index 00000000..2a52a84e --- /dev/null +++ b/apps/desktop/playwright.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from '@playwright/test'; + +/** + * Playwright config for the Readied Electron app. + * + * Tests are end-to-end against the built Electron bundle in `out/`, + * launched via Playwright's `electron` API (`_electron.launch`). + * + * Before running: `pnpm build` to produce `out/main/index.js`. + * + * Local: `pnpm e2e` — headless against the build + * `pnpm e2e:headed` — open the actual window + * + * On CI we run linux + xvfb. See .github/workflows/ci.yml. + */ +export default defineConfig({ + testDir: './e2e', + fullyParallel: false, // Electron app state is shared; tests run serially + workers: 1, + retries: process.env.CI ? 2 : 0, + timeout: 60_000, + expect: { timeout: 10_000 }, + reporter: process.env.CI ? [['list'], ['html', { open: 'never' }]] : 'list', + use: { + trace: 'retain-on-failure', + video: 'retain-on-failure', + }, +}); diff --git a/apps/desktop/src/main/handlers/aiKeyHandlers.ts b/apps/desktop/src/main/handlers/aiKeyHandlers.ts index 74286126..ad084928 100644 --- a/apps/desktop/src/main/handlers/aiKeyHandlers.ts +++ b/apps/desktop/src/main/handlers/aiKeyHandlers.ts @@ -2,35 +2,62 @@ * AI Key Storage IPC Handlers * * Handles saving, retrieving, and managing AI provider API keys. + * + * Inputs are validated at the IPC boundary via Zod. Renderer-supplied + * provider names and keys are bounded in length and shape — a malformed + * payload throws an IpcValidationError before ever reaching aiKeyStorage. */ -import { ipcMain } from 'electron'; +import { z } from 'zod'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { AiKeyStorage } from './types.js'; export interface AiKeyHandlerDeps { aiKeyStorage: AiKeyStorage; } +// A provider name is short, kebab-case-ish, and ASCII. Cap at 64 to defend +// against accidental large inputs. +const ProviderSchema = z + .string() + .min(1) + .max(64) + .regex(/^[a-zA-Z0-9_-]+$/, 'Provider name must be alphanumeric (with _ or -)'); + +// Keys can be long (sk-... up to a few hundred chars on some providers); +// 4096 is well above anything real and well below "this is junk". +const ApiKeySchema = z.string().min(1).max(4096); + export function registerAiKeyHandlers(deps: AiKeyHandlerDeps): void { const { aiKeyStorage } = deps; - ipcMain.handle('ai:saveKey', async (_event, provider: string, apiKey: string) => { - await aiKeyStorage.saveKey(provider, apiKey); + defineIpcHandler({ + channel: 'ai:saveKey', + args: z.tuple([ProviderSchema, ApiKeySchema]), + handler: (provider, apiKey) => aiKeyStorage.saveKey(provider, apiKey), }); - ipcMain.handle('ai:getKey', async (_event, provider: string) => { - return aiKeyStorage.getKey(provider); + defineIpcHandler({ + channel: 'ai:getKey', + args: z.tuple([ProviderSchema]), + handler: provider => aiKeyStorage.getKey(provider), }); - ipcMain.handle('ai:removeKey', async (_event, provider: string) => { - await aiKeyStorage.removeKey(provider); + defineIpcHandler({ + channel: 'ai:removeKey', + args: z.tuple([ProviderSchema]), + handler: provider => aiKeyStorage.removeKey(provider), }); - ipcMain.handle('ai:hasKey', async (_event, provider: string) => { - return aiKeyStorage.hasKey(provider); + defineIpcHandler({ + channel: 'ai:hasKey', + args: z.tuple([ProviderSchema]), + handler: provider => aiKeyStorage.hasKey(provider), }); - ipcMain.handle('ai:listConnectedProviders', async () => { - return aiKeyStorage.listProviders(); + defineIpcHandler({ + channel: 'ai:listConnectedProviders', + args: z.tuple([]), + handler: () => aiKeyStorage.listProviders(), }); } diff --git a/apps/desktop/src/main/handlers/authSyncHandlers.ts b/apps/desktop/src/main/handlers/authSyncHandlers.ts index 7cb3517d..472616d4 100644 --- a/apps/desktop/src/main/handlers/authSyncHandlers.ts +++ b/apps/desktop/src/main/handlers/authSyncHandlers.ts @@ -5,7 +5,9 @@ * subscription/billing, and device management. */ -import { ipcMain, shell } from 'electron'; +import { shell } from 'electron'; +import { z } from 'zod'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { ApiClient, EncryptionService, @@ -22,6 +24,29 @@ export interface AuthSyncHandlerDeps { broadcastToWindows: BroadcastFn; } +const EmailSchema = z.string().email().max(254); +const TokenSchema = z.string().min(1).max(2048); +const PassphraseSchema = z.string().min(1).max(1024); +const RecoveryKeySchema = z.string().min(8).max(512); +const IdSchema = z.string().min(1).max(128); +const NameSchema = z.string().min(1).max(128); +const KeyHexSchema = z + .string() + .min(32) + .max(256) + .regex(/^[a-f0-9]+$/i); +const UrlSchema = z.string().url().max(2048); + +const SyncChangeSchema = z.object({ + noteId: IdSchema, + operation: z.enum(['create', 'update', 'delete']), + content: z + .string() + .max(10 * 1024 * 1024) + .optional(), + localVersion: z.number().int().nonnegative().optional(), +}); + export function registerAuthSyncHandlers(deps: AuthSyncHandlerDeps): void { const { apiClient: client, @@ -30,7 +55,6 @@ export function registerAuthSyncHandlers(deps: AuthSyncHandlerDeps): void { encryptionService: encryption, } = deps; - // Broadcast sync status events to all renderer windows sync.onStatusChange(event => { deps.broadcastToWindows('sync:status-changed', event); }); @@ -39,556 +63,603 @@ export function registerAuthSyncHandlers(deps: AuthSyncHandlerDeps): void { // Authentication // ═══════════════════════════════════════════════════════════════════════════ - // Request magic link email - ipcMain.handle('auth:requestMagicLink', async (_event, email: string) => { - try { - await client.requestMagicLink(email); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to request magic link', - }; - } - }); - - // Verify magic link token and save tokens - ipcMain.handle('auth:verify', async (_event, token: string) => { - try { - const result = await client.verifyMagicLink(token); - await storage.saveTokens(result.accessToken, result.refreshToken); - return { success: true, user: result.user }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to verify token', - }; - } - }); - - // Get current session - ipcMain.handle('auth:getSession', async () => { - try { - const hasTokens = await storage.hasTokens(); - if (!hasTokens) { + defineIpcHandler({ + channel: 'auth:requestMagicLink', + args: z.tuple([EmailSchema]), + handler: async email => { + try { + await client.requestMagicLink(email); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to request magic link', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'auth:verify', + args: z.tuple([TokenSchema]), + handler: async token => { + try { + const result = await client.verifyMagicLink(token); + await storage.saveTokens(result.accessToken, result.refreshToken); + return { success: true, user: result.user }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to verify token', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'auth:getSession', + args: z.tuple([]), + handler: async () => { + try { + const hasTokens = await storage.hasTokens(); + if (!hasTokens) return null; + const user = await client.getCurrentUser(); + return { user }; + } catch { + await storage.clearTokens(); return null; } + }, + }); - const user = await client.getCurrentUser(); - return { user }; - } catch (_error) { - // If session is invalid, clear tokens - await storage.clearTokens(); - return null; - } - }); - - // Logout and clear tokens - ipcMain.handle('auth:logout', async () => { - try { - // Abort any in-flight sync operations before clearing tokens - sync?.stopAutoSync(); - await storage.clearTokens(); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to logout', - }; - } - }); - - // Refresh access token - ipcMain.handle('auth:refreshToken', async () => { - try { - const refreshed = await client.refreshAccessToken(); - return { success: refreshed }; - } catch (_error) { - return { success: false }; - } + defineIpcHandler({ + channel: 'auth:logout', + args: z.tuple([]), + handler: async () => { + try { + sync?.stopAutoSync(); + await storage.clearTokens(); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to logout', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'auth:refreshToken', + args: z.tuple([]), + handler: async () => { + try { + const refreshed = await client.refreshAccessToken(); + return { success: refreshed }; + } catch { + return { success: false }; + } + }, }); // ═══════════════════════════════════════════════════════════════════════════ // Sync // ═══════════════════════════════════════════════════════════════════════════ - // Pull changes from server - ipcMain.handle('sync:pull', async () => { - try { - const result = await sync.pull(); - return { - success: result.success, - changes: result.changes, - cursor: result.cursor, - hasMore: result.hasMore, - conflicts: result.conflicts, - error: result.error, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to pull changes', - }; - } - }); - - // Push changes to server - ipcMain.handle( - 'sync:push', - async ( - _event, - changes: Array<{ - noteId: string; - operation: 'create' | 'update' | 'delete'; - content?: string; - localVersion?: number; - }> - ) => { + defineIpcHandler({ + channel: 'sync:pull', + args: z.tuple([]), + handler: async () => { try { - const result = await sync.push(changes); + const result = await sync.pull(); return { success: result.success, - results: result.results, + changes: result.changes, + cursor: result.cursor, + hasMore: result.hasMore, + conflicts: result.conflicts, error: result.error, }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to pull changes', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:push', + args: z.tuple([z.array(SyncChangeSchema).max(100000)]), + handler: async changes => { + try { + const result = await sync.push(changes); + return { success: result.success, results: result.results, error: result.error }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Failed to push changes', }; } - } - ); - - // Perform full sync (pull + push) - ipcMain.handle('sync:syncNow', async () => { - try { - const result = await sync.syncNow(); - return result; - } catch (error) { - return { - success: false, - changesApplied: 0, - changesPushed: 0, - conflicts: [], - error: error instanceof Error ? error.message : 'Sync failed', - }; - } - }); - - // Get sync status - ipcMain.handle('sync:status', async () => { - try { - const state = sync.getState(); - return { - success: true, - cursor: state.cursor, - lastSyncAt: state.lastSyncAt, - isSyncing: state.isSyncing, - lastError: state.lastError, - consecutiveFailures: state.consecutiveFailures, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to get sync status', - }; - } - }); - - // Get pending change count (offline queue size) - ipcMain.handle('sync:pendingCount', async () => { - try { - return { success: true, count: sync.getPendingCount() }; - } catch (error) { - return { success: false, count: 0, error: error instanceof Error ? error.message : 'Failed' }; - } - }); - - // Resolve conflict - ipcMain.handle( - 'sync:resolveConflict', - async (_event, noteId: string, resolution: 'local' | 'remote') => { + }, + }); + + defineIpcHandler({ + channel: 'sync:syncNow', + args: z.tuple([]), + handler: async () => { try { - await sync.resolveConflict(noteId, resolution); + return await sync.syncNow(); + } catch (error) { + return { + success: false, + changesApplied: 0, + changesPushed: 0, + conflicts: [], + error: error instanceof Error ? error.message : 'Sync failed', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:status', + args: z.tuple([]), + handler: () => { + try { + const state = sync.getState(); return { success: true, + cursor: state.cursor, + lastSyncAt: state.lastSyncAt, + isSyncing: state.isSyncing, + lastError: state.lastError, + consecutiveFailures: state.consecutiveFailures, }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get sync status', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:pendingCount', + args: z.tuple([]), + handler: () => { + try { + return { success: true, count: sync.getPendingCount() }; + } catch (error) { + return { + success: false, + count: 0, + error: error instanceof Error ? error.message : 'Failed', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:resolveConflict', + args: z.tuple([IdSchema, z.enum(['local', 'remote'])]), + handler: async (noteId, resolution) => { + try { + await sync.resolveConflict(noteId, resolution); + return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Failed to resolve conflict', }; } - } - ); - - // Start auto-sync - ipcMain.handle('sync:startAutoSync', async (_event, intervalMs?: number) => { - try { - sync.startAutoSync(intervalMs); - return { - success: true, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to start auto-sync', - }; - } - }); - - // Stop auto-sync - ipcMain.handle('sync:stopAutoSync', async () => { - try { - sync.stopAutoSync(); - return { - success: true, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to stop auto-sync', - }; - } - }); - - // Tag sync - pull - ipcMain.handle('sync:pullTags', async () => { - try { - return await sync.pullTags(); - } catch (error) { - return { success: false, applied: 0, error: String(error) }; - } - }); - - // Tag sync - push - ipcMain.handle('sync:pushTags', async () => { - try { - return await sync.pushTags(); - } catch (error) { - return { success: false, pushed: 0, error: String(error) }; - } - }); - - ipcMain.handle('sync:history', async (_event, limit?: number) => { - try { - const history = sync.getSyncHistory(limit); - return { success: true, history }; - } catch (error) { - return { - success: false, - history: [], - error: error instanceof Error ? error.message : 'Failed to get sync history', - }; - } + }, + }); + + defineIpcHandler({ + channel: 'sync:startAutoSync', + args: z.tuple([ + z + .number() + .int() + .min(1000) + .max(24 * 60 * 60 * 1000) + .optional(), + ]), + handler: intervalMs => { + try { + sync.startAutoSync(intervalMs); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to start auto-sync', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:stopAutoSync', + args: z.tuple([]), + handler: () => { + try { + sync.stopAutoSync(); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to stop auto-sync', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:pullTags', + args: z.tuple([]), + handler: async () => { + try { + return await sync.pullTags(); + } catch (error) { + return { success: false, applied: 0, error: String(error) }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:pushTags', + args: z.tuple([]), + handler: async () => { + try { + return await sync.pushTags(); + } catch (error) { + return { success: false, pushed: 0, error: String(error) }; + } + }, + }); + + defineIpcHandler({ + channel: 'sync:history', + args: z.tuple([z.number().int().positive().max(10000).optional()]), + handler: limit => { + try { + const history = sync.getSyncHistory(limit); + return { success: true, history }; + } catch (error) { + return { + success: false, + history: [], + error: error instanceof Error ? error.message : 'Failed to get sync history', + }; + } + }, }); // ═══════════════════════════════════════════════════════════════════════════ // E2EE Key Management // ═══════════════════════════════════════════════════════════════════════════ - // Check if encryption is ready (CEK cached locally) - ipcMain.handle('encryption:isReady', async () => { - return { ready: encryption?.isReady() ?? false }; - }); - - // Check if this is a first-time setup or existing user - ipcMain.handle('encryption:getKeyStatus', async () => { - try { - const serverKeys = await client.getEncryptionKeys(); - const hasLocalKey = encryption?.isReady() ?? false; - const hasLegacyKey = encryption?.hasLegacyKey() ?? false; - - return { - success: true, - hasServerKeys: serverKeys.exists, - hasLocalKey, - hasLegacyKey, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to get key status', - }; - } - }); - - // First device: set up encryption keys with passphrase - ipcMain.handle('encryption:setupKeys', async (_event, passphrase: string) => { - try { - if (!encryption) throw new Error('Encryption service not available'); - - const result = await encryption.setupKeys(passphrase); - - // Upload to server - await client.setEncryptionKeys({ - salt: result.salt, - wrappedCek: result.wrappedCek, - wrappedCekRecovery: result.wrappedCekRecovery, - kdfParams: result.kdfParams, - }); - - return { - success: true, - recoveryKey: result.recoveryKey, // Show once to user! - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to setup encryption keys', - }; - } - }); - - // New device: unlock with passphrase - ipcMain.handle('encryption:unlockWithPassphrase', async (_event, passphrase: string) => { - try { - if (!encryption) throw new Error('Encryption service not available'); - - const serverKeys = await client.getEncryptionKeys(); - if ( - !serverKeys.exists || - !serverKeys.salt || - !serverKeys.wrappedCek || - !serverKeys.kdfParams - ) { - return { success: false, error: 'No encryption keys found on server' }; - } - - await encryption.unlockWithPassphrase( - passphrase, - serverKeys.salt, - serverKeys.wrappedCek, - serverKeys.kdfParams - ); - - return { success: true }; - } catch (error) { - const msg = error instanceof Error ? error.message : 'Failed to unlock'; - const isWrongPassphrase = msg.includes('incorrect passphrase') || msg.includes('unwrap'); - return { - success: false, - wrongPassphrase: isWrongPassphrase, - error: isWrongPassphrase ? 'Incorrect passphrase' : msg, - }; - } - }); - - // Unlock with recovery key - ipcMain.handle('encryption:unlockWithRecoveryKey', async (_event, recoveryKey: string) => { - try { - if (!encryption) throw new Error('Encryption service not available'); - - const serverKeys = await client.getEncryptionKeys(); - if (!serverKeys.exists || !serverKeys.wrappedCekRecovery) { - return { success: false, error: 'No recovery key found on server' }; - } - - await encryption.unlockWithRecoveryKey(recoveryKey, serverKeys.wrappedCekRecovery); - - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to unlock with recovery key', - }; - } - }); - - // Migrate legacy per-device key to key hierarchy - ipcMain.handle('encryption:migrateLegacyKey', async (_event, passphrase: string) => { - try { - if (!encryption) throw new Error('Encryption service not available'); - - const result = await encryption.migrateLegacyKey(passphrase); - - // Upload to server - await client.setEncryptionKeys({ - salt: result.salt, - wrappedCek: result.wrappedCek, - wrappedCekRecovery: result.wrappedCekRecovery, - kdfParams: result.kdfParams, - }); - - return { - success: true, - recoveryKey: result.recoveryKey, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to migrate legacy key', - }; - } - }); - - // Change passphrase (re-wrap CEK) - ipcMain.handle('encryption:changePassphrase', async (_event, newPassphrase: string) => { - try { - if (!encryption) throw new Error('Encryption service not available'); - - const result = await encryption.changePassphrase(newPassphrase); - - // Upload new wrapped key to server - await client.setEncryptionKeys({ - salt: result.salt, - wrappedCek: result.wrappedCek, - kdfParams: result.kdfParams, - }); - - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to change passphrase', - }; - } + defineIpcHandler({ + channel: 'encryption:isReady', + args: z.tuple([]), + handler: () => ({ ready: encryption?.isReady() ?? false }), + }); + + defineIpcHandler({ + channel: 'encryption:getKeyStatus', + args: z.tuple([]), + handler: async () => { + try { + const serverKeys = await client.getEncryptionKeys(); + const hasLocalKey = encryption?.isReady() ?? false; + const hasLegacyKey = encryption?.hasLegacyKey() ?? false; + return { + success: true, + hasServerKeys: serverKeys.exists, + hasLocalKey, + hasLegacyKey, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get key status', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'encryption:setupKeys', + args: z.tuple([PassphraseSchema]), + handler: async passphrase => { + try { + if (!encryption) throw new Error('Encryption service not available'); + const result = await encryption.setupKeys(passphrase); + await client.setEncryptionKeys({ + salt: result.salt, + wrappedCek: result.wrappedCek, + wrappedCekRecovery: result.wrappedCekRecovery, + kdfParams: result.kdfParams, + }); + return { success: true, recoveryKey: result.recoveryKey }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to setup encryption keys', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'encryption:unlockWithPassphrase', + args: z.tuple([PassphraseSchema]), + handler: async passphrase => { + try { + if (!encryption) throw new Error('Encryption service not available'); + const serverKeys = await client.getEncryptionKeys(); + if ( + !serverKeys.exists || + !serverKeys.salt || + !serverKeys.wrappedCek || + !serverKeys.kdfParams + ) { + return { success: false, error: 'No encryption keys found on server' }; + } + await encryption.unlockWithPassphrase( + passphrase, + serverKeys.salt, + serverKeys.wrappedCek, + serverKeys.kdfParams + ); + return { success: true }; + } catch (error) { + const msg = error instanceof Error ? error.message : 'Failed to unlock'; + const isWrongPassphrase = msg.includes('incorrect passphrase') || msg.includes('unwrap'); + return { + success: false, + wrongPassphrase: isWrongPassphrase, + error: isWrongPassphrase ? 'Incorrect passphrase' : msg, + }; + } + }, + }); + + defineIpcHandler({ + channel: 'encryption:unlockWithRecoveryKey', + args: z.tuple([RecoveryKeySchema]), + handler: async recoveryKey => { + try { + if (!encryption) throw new Error('Encryption service not available'); + const serverKeys = await client.getEncryptionKeys(); + if (!serverKeys.exists || !serverKeys.wrappedCekRecovery) { + return { success: false, error: 'No recovery key found on server' }; + } + await encryption.unlockWithRecoveryKey(recoveryKey, serverKeys.wrappedCekRecovery); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to unlock with recovery key', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'encryption:migrateLegacyKey', + args: z.tuple([PassphraseSchema]), + handler: async passphrase => { + try { + if (!encryption) throw new Error('Encryption service not available'); + const result = await encryption.migrateLegacyKey(passphrase); + await client.setEncryptionKeys({ + salt: result.salt, + wrappedCek: result.wrappedCek, + wrappedCekRecovery: result.wrappedCekRecovery, + kdfParams: result.kdfParams, + }); + return { success: true, recoveryKey: result.recoveryKey }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to migrate legacy key', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'encryption:changePassphrase', + args: z.tuple([PassphraseSchema]), + handler: async newPassphrase => { + try { + if (!encryption) throw new Error('Encryption service not available'); + const result = await encryption.changePassphrase(newPassphrase); + await client.setEncryptionKeys({ + salt: result.salt, + wrappedCek: result.wrappedCek, + kdfParams: result.kdfParams, + }); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to change passphrase', + }; + } + }, }); // ═══════════════════════════════════════════════════════════════════════════ // Subscription // ═══════════════════════════════════════════════════════════════════════════ - // Get subscription status - ipcMain.handle('subscription:getStatus', async () => { - try { - const status = await client.getSubscriptionStatus(); - return { - success: true, - status, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to get subscription status', - }; - } - }); - - // Open Stripe billing portal - ipcMain.handle('subscription:openPortal', async (_event, returnUrl: string) => { - try { - const { url } = await client.createPortalSession(returnUrl); - void shell.openExternal(url); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to open billing portal', - }; - } - }); - - // Open checkout (placeholder - opens pricing page) - ipcMain.handle('subscription:openCheckout', async () => { - try { - void shell.openExternal('https://readied.app/pricing'); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to open checkout', - }; - } + defineIpcHandler({ + channel: 'subscription:getStatus', + args: z.tuple([]), + handler: async () => { + try { + const status = await client.getSubscriptionStatus(); + return { success: true, status }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get subscription status', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'subscription:openPortal', + args: z.tuple([UrlSchema]), + handler: async returnUrl => { + try { + const { url } = await client.createPortalSession(returnUrl); + void shell.openExternal(url); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to open billing portal', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'subscription:openCheckout', + args: z.tuple([]), + handler: () => { + try { + void shell.openExternal('https://readied.app/pricing'); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to open checkout', + }; + } + }, }); // ═══════════════════════════════════════════════════════════════════════════ // Devices // ═══════════════════════════════════════════════════════════════════════════ - ipcMain.handle('devices:list', async () => { - try { - const result = await client.listDevices(); - return result.devices; - } catch (_error) { - return []; - } - }); - - ipcMain.handle('devices:rename', async (_event, deviceId: string, name: string) => { - try { - await client.renameDevice(deviceId, name); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to rename device', - }; - } - }); - - ipcMain.handle('devices:revoke', async (_event, deviceId: string) => { - try { - await client.revokeDevice(deviceId); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to revoke device', - }; - } - }); - - ipcMain.handle('devices:revokeOthers', async () => { - try { - const result = await client.revokeOtherDevices(); - return { success: true, revokedCount: result.revokedCount }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to revoke devices', - }; - } - }); - - ipcMain.handle('devices:getCurrent', async () => { - try { - const result = await client.listDevices(); - return result.devices.find(d => d.isCurrent) ?? null; - } catch (_error) { - return null; - } + defineIpcHandler({ + channel: 'devices:list', + args: z.tuple([]), + handler: async () => { + try { + const result = await client.listDevices(); + return result.devices; + } catch { + return []; + } + }, + }); + + defineIpcHandler({ + channel: 'devices:rename', + args: z.tuple([IdSchema, NameSchema]), + handler: async (deviceId, name) => { + try { + await client.renameDevice(deviceId, name); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to rename device', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'devices:revoke', + args: z.tuple([IdSchema]), + handler: async deviceId => { + try { + await client.revokeDevice(deviceId); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to revoke device', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'devices:revokeOthers', + args: z.tuple([]), + handler: async () => { + try { + const result = await client.revokeOtherDevices(); + return { success: true, revokedCount: result.revokedCount }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to revoke devices', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'devices:getCurrent', + args: z.tuple([]), + handler: async () => { + try { + const result = await client.listDevices(); + return result.devices.find(d => d.isCurrent) ?? null; + } catch { + return null; + } + }, }); // ═══════════════════════════════════════════════════════════════════════════ - // Encryption Key Management + // Encryption Key Management (export/import) // ═══════════════════════════════════════════════════════════════════════════ - // Export encryption key (for backup) - ipcMain.handle('encryption:exportKey', async () => { - try { - if (!encryption) { - throw new Error('Encryption service not initialized'); - } - const keyHex = encryption.exportKey(); - return { - success: true, - key: keyHex, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to export encryption key', - }; - } - }); - - // Import encryption key (for restore) - ipcMain.handle('encryption:importKey', async (_event, keyHex: string) => { - try { - if (!encryption) { - throw new Error('Encryption service not initialized'); - } - await encryption.importKey(keyHex); - return { - success: true, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to import encryption key', - }; - } + defineIpcHandler({ + channel: 'encryption:exportKey', + args: z.tuple([]), + handler: () => { + try { + if (!encryption) throw new Error('Encryption service not initialized'); + const keyHex = encryption.exportKey(); + return { success: true, key: keyHex }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to export encryption key', + }; + } + }, + }); + + defineIpcHandler({ + channel: 'encryption:importKey', + args: z.tuple([KeyHexSchema]), + handler: async keyHex => { + try { + if (!encryption) throw new Error('Encryption service not initialized'); + await encryption.importKey(keyHex); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to import encryption key', + }; + } + }, }); } diff --git a/apps/desktop/src/main/handlers/dataHandlers.ts b/apps/desktop/src/main/handlers/dataHandlers.ts index 7177ce7b..b2f7d1d7 100644 --- a/apps/desktop/src/main/handlers/dataHandlers.ts +++ b/apps/desktop/src/main/handlers/dataHandlers.ts @@ -6,7 +6,9 @@ import { join } from 'path'; import { writeFile } from 'fs/promises'; +import { copyFileSync, existsSync, unlinkSync } from 'fs'; import { ipcMain, dialog, shell, app } from 'electron'; +import { z } from 'zod'; import { createBackup, listBackups, @@ -19,6 +21,7 @@ import { import { createDatabase, allMigrations } from '@readied/storage-sqlite'; import { runMigrations } from '@readied/storage-core'; import { createNoteOperation } from '@readied/core'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { SQLiteNoteRepository, Database } from './types.js'; export interface DataHandlerDeps { @@ -33,81 +36,148 @@ export interface DataHandlerDeps { export function registerDataHandlers(deps: DataHandlerDeps): void { const { dataPaths: paths, noteRepository: repo, getDb, setDb } = deps; - // Create backup - ipcMain.handle('data:backup', async () => { - return createBackup({ - backupDir: paths.backups, - databasePath: paths.database, - }); + defineIpcHandler({ + channel: 'data:backup', + args: z.tuple([]), + handler: () => + createBackup({ + backupDir: paths.backups, + databasePath: paths.database, + }), }); - // List backups - ipcMain.handle('data:backups:list', async () => { - return listBackups(paths.backups); + defineIpcHandler({ + channel: 'data:backups:list', + args: z.tuple([]), + handler: () => listBackups(paths.backups), }); - // Restore from backup + // Restore uses ipcMain.handle raw because the integrity-check rollback + // path is non-trivial state management — see PR #271 for the rationale. + // Validation: backupPath must be a non-empty string. We don't constrain + // it further (it comes from a native dialog), but the rollback logic + // is what guarantees safety, not the schema. ipcMain.handle('data:backup:restore', async (_event, backupPath: string) => { - // Close current database connection const currentDb = getDb(); if (currentDb) { currentDb.close(); } + // Copies backup over the live db file and writes a `.pre-restore` safety + // copy of the previous live db (used by the rollback path below). const result = restoreBackup(backupPath, paths.database); + if (!result.success) { + // restoreBackup never touched the live db, just reopen it. + setDb(createDatabase(paths.database)); + return result; + } - // Reconnect to database - const newDb = createDatabase(paths.database); - runMigrations(newDb, allMigrations); - setDb(newDb); + const safetyPath = paths.database + '.pre-restore'; + const rollback = (reason: string): typeof result => { + if (existsSync(safetyPath)) { + copyFileSync(safetyPath, paths.database); + } + setDb(createDatabase(paths.database)); + return { success: false, error: reason }; + }; - return result; - }); + let newDb: ReturnType; + try { + newDb = createDatabase(paths.database); + } catch (err) { + return rollback( + `Could not open restored database: ${err instanceof Error ? err.message : String(err)}` + ); + } - // Export notes - ipcMain.handle('data:export', async () => { - // Show save dialog - const { filePath, canceled } = await dialog.showSaveDialog({ - title: 'Export Notes', - defaultPath: join(app.getPath('documents'), 'readied-export'), - buttonLabel: 'Export', - }); - - if (canceled || !filePath) { - return { success: false, error: 'Export cancelled' }; + // PRAGMA integrity_check returns a single row `{ integrity_check: 'ok' }` + // on a healthy database, or one or more rows describing the corruption. + // We refuse to swap to a corrupt restore and roll back to the safety copy. + try { + const row = newDb.prepare<{ integrity_check: string }>('PRAGMA integrity_check').get(); + if (row?.integrity_check !== 'ok') { + newDb.close(); + return rollback( + `Backup failed integrity check (${row?.integrity_check ?? 'unknown error'}). Previous database has been restored.` + ); + } + } catch (err) { + newDb.close(); + return rollback( + `Backup integrity check threw: ${err instanceof Error ? err.message : String(err)}. Previous database has been restored.` + ); } - // Get all notes - const notes = await repo.list({ archived: 'all' }); - const snapshots = notes.map(note => ({ - id: note.id, - content: note.content, - title: note.title, // Use structural title - createdAt: note.metadata.createdAt, - updatedAt: note.metadata.updatedAt, - tags: [...note.metadata.tags], - wordCount: note.metadata.wordCount, - archivedAt: note.metadata.archivedAt, - })); - - const result = exportNotes(snapshots, { - outputDir: filePath, - appVersion: app.getVersion(), - includeArchived: true, - }); - - if (result.success) { - // Open the export folder - shell.showItemInFolder(filePath); + // Backup is intact — apply migrations to bring older schemas current. + try { + runMigrations(newDb, allMigrations); + } catch (err) { + newDb.close(); + return rollback( + `Migrations failed on restored database: ${err instanceof Error ? err.message : String(err)}. Previous database has been restored.` + ); + } + + setDb(newDb); + + // Restore succeeded — discard the safety copy. + if (existsSync(safetyPath)) { + try { + unlinkSync(safetyPath); + } catch { + // best-effort cleanup; not fatal + } } return result; }); - // Export single note to file - ipcMain.handle( - 'data:exportNote', - async (_event: Electron.IpcMainInvokeEvent, content: string, suggestedName: string) => { + defineIpcHandler({ + channel: 'data:export', + args: z.tuple([]), + handler: async () => { + // Show save dialog + const { filePath, canceled } = await dialog.showSaveDialog({ + title: 'Export Notes', + defaultPath: join(app.getPath('documents'), 'readied-export'), + buttonLabel: 'Export', + }); + + if (canceled || !filePath) { + return { success: false, error: 'Export cancelled' }; + } + + // Get all notes + const notes = await repo.list({ archived: 'all' }); + const snapshots = notes.map(note => ({ + id: note.id, + content: note.content, + title: note.title, // Use structural title + createdAt: note.metadata.createdAt, + updatedAt: note.metadata.updatedAt, + tags: [...note.metadata.tags], + wordCount: note.metadata.wordCount, + archivedAt: note.metadata.archivedAt, + })); + + const result = exportNotes(snapshots, { + outputDir: filePath, + appVersion: app.getVersion(), + includeArchived: true, + }); + + if (result.success) { + shell.showItemInFolder(filePath); + } + + return result; + }, + }); + + defineIpcHandler({ + channel: 'data:exportNote', + args: z.tuple([z.string().max(1024 * 1024), z.string().max(512)]), + handler: async (content, suggestedName) => { let safeName = suggestedName .normalize('NFC') @@ -137,71 +207,78 @@ export function registerDataHandlers(deps: DataHandlerDeps): void { error: error instanceof Error ? error.message : 'Failed to write file', }; } - } - ); - - // Import notes - ipcMain.handle('data:import', async () => { - // Show folder selection dialog - const { filePaths, canceled } = await dialog.showOpenDialog({ - title: 'Import Notes', - properties: ['openDirectory'], - buttonLabel: 'Import', - }); - - const sourceDir = filePaths[0]; - if (canceled || !sourceDir) { - return { success: false, error: 'Import cancelled' }; - } + }, + }); - const importType = detectImportType(sourceDir); + defineIpcHandler({ + channel: 'data:import', + args: z.tuple([]), + handler: async () => { + // Show folder selection dialog + const { filePaths, canceled } = await dialog.showOpenDialog({ + title: 'Import Notes', + properties: ['openDirectory'], + buttonLabel: 'Import', + }); - const result = importNotes({ - sourceDir, - type: importType, - recursive: true, - }); + const sourceDir = filePaths[0]; + if (canceled || !sourceDir) { + return { success: false, error: 'Import cancelled' }; + } - if (!result.success || !result.notes) { - return result; - } + const importType = detectImportType(sourceDir); - // Import each note - let imported = 0; - for (const imported_note of result.notes) { - try { - await createNoteOperation( - { - content: imported_note.content, - }, - repo - ); - imported++; - } catch { - // Skip notes that fail to import + const result = importNotes({ + sourceDir, + type: importType, + recursive: true, + }); + + if (!result.success || !result.notes) { + return result; } - } - return { - success: true, - noteCount: imported, - skipped: result.skipped, - }; + // Import each note + let imported = 0; + for (const imported_note of result.notes) { + try { + await createNoteOperation( + { + content: imported_note.content, + }, + repo + ); + imported++; + } catch { + // Skip notes that fail to import + } + } + + return { + success: true, + noteCount: imported, + skipped: result.skipped, + }; + }, }); - // Get data paths info - ipcMain.handle('data:paths', async () => { - return { + defineIpcHandler({ + channel: 'data:paths', + args: z.tuple([]), + handler: () => ({ root: paths.root, database: paths.database, backups: paths.backups, logs: paths.logs, - }; + }), }); - // Open data folder in system file manager - ipcMain.handle('data:openFolder', async () => { - void shell.openPath(paths.root); - return { success: true }; + defineIpcHandler({ + channel: 'data:openFolder', + args: z.tuple([]), + handler: () => { + void shell.openPath(paths.root); + return { success: true }; + }, }); } diff --git a/apps/desktop/src/main/handlers/gitHandlers.ts b/apps/desktop/src/main/handlers/gitHandlers.ts index c99520d4..431896d0 100644 --- a/apps/desktop/src/main/handlers/gitHandlers.ts +++ b/apps/desktop/src/main/handlers/gitHandlers.ts @@ -4,113 +4,133 @@ * Handles git operations for git-backed notebooks. */ -import { ipcMain } from 'electron'; +import { z } from 'zod'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { GitService } from './types.js'; export interface GitHandlerDeps { gitService: GitService; } +const IdSchema = z.string().min(1).max(128); +// SHAs are hex; allow short-SHAs (≥7) up to full 40-char. +const ShaSchema = z + .string() + .min(7) + .max(40) + .regex(/^[a-f0-9]+$/i); +// Commit messages can be long but not absurd. +const CommitMessageSchema = z.string().min(1).max(8192); +// Note file content cap matches the share payload cap. +const NoteContentSchema = z.string().max(1024 * 1024); + export function registerGitHandlers(deps: GitHandlerDeps): void { const { gitService: git } = deps; - // Initialize git repository for a notebook - ipcMain.handle('git:init', async (_event, notebookId: string) => { - try { - const repoPath = await git.initRepository(notebookId); - return { - success: true, - repoPath, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to initialize git repository', - }; - } + defineIpcHandler({ + channel: 'git:init', + args: z.tuple([IdSchema]), + handler: async notebookId => { + try { + const repoPath = await git.initRepository(notebookId); + return { success: true, repoPath }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to initialize git repository', + }; + } + }, }); - // Check if notebook has git repository - ipcMain.handle('git:isRepo', async (_event, notebookId: string) => { - try { - const isRepo = await git.isGitRepository(notebookId); - return { success: true, isRepo }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to check git repository', - }; - } + defineIpcHandler({ + channel: 'git:isRepo', + args: z.tuple([IdSchema]), + handler: async notebookId => { + try { + const isRepo = await git.isGitRepository(notebookId); + return { success: true, isRepo }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to check git repository', + }; + } + }, }); - // Commit changes - ipcMain.handle( - 'git:commit', - async (_event, notebookId: string, message: string, files?: string[]) => { + defineIpcHandler({ + channel: 'git:commit', + args: z.tuple([ + IdSchema, + CommitMessageSchema, + z.array(z.string().max(1024)).max(10000).optional(), + ]), + handler: async (notebookId, message, files) => { try { const sha = await git.commit(notebookId, message, files); - return { - success: true, - sha, - }; + return { success: true, sha }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Failed to commit changes', }; } - } - ); + }, + }); - // Get commit history - ipcMain.handle('git:log', async (_event, notebookId: string, limit?: number) => { - try { - const commits = await git.log(notebookId, limit); - return { - success: true, - commits, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to get commit history', - }; - } + defineIpcHandler({ + channel: 'git:log', + args: z.tuple([IdSchema, z.number().int().positive().max(10000).optional()]), + handler: async (notebookId, limit) => { + try { + const commits = await git.log(notebookId, limit); + return { success: true, commits }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get commit history', + }; + } + }, }); - // Get repository status - ipcMain.handle('git:status', async (_event, notebookId: string) => { - try { - const status = await git.status(notebookId); - return { - success: true, - status, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to get repository status', - }; - } + defineIpcHandler({ + channel: 'git:status', + args: z.tuple([IdSchema]), + handler: async notebookId => { + try { + const status = await git.status(notebookId); + return { success: true, status }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get repository status', + }; + } + }, }); - // Checkout (revert to) a specific commit - ipcMain.handle('git:checkout', async (_event, notebookId: string, commitSha: string) => { - try { - await git.checkout(notebookId, commitSha); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to checkout commit', - }; - } + defineIpcHandler({ + channel: 'git:checkout', + args: z.tuple([IdSchema, ShaSchema]), + handler: async (notebookId, commitSha) => { + try { + await git.checkout(notebookId, commitSha); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to checkout commit', + }; + } + }, }); - // Write note file to git repository - ipcMain.handle( - 'git:writeNote', - async (_event, notebookId: string, noteId: string, content: string) => { + defineIpcHandler({ + channel: 'git:writeNote', + args: z.tuple([IdSchema, IdSchema, NoteContentSchema]), + handler: async (notebookId, noteId, content) => { try { await git.writeNoteFile(notebookId, noteId, content); return { success: true }; @@ -120,35 +140,38 @@ export function registerGitHandlers(deps: GitHandlerDeps): void { error: error instanceof Error ? error.message : 'Failed to write note file', }; } - } - ); + }, + }); - // Read note file from git repository - ipcMain.handle('git:readNote', async (_event, notebookId: string, noteId: string) => { - try { - const content = await git.readNoteFile(notebookId, noteId); - return { - success: true, - content, - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to read note file', - }; - } + defineIpcHandler({ + channel: 'git:readNote', + args: z.tuple([IdSchema, IdSchema]), + handler: async (notebookId, noteId) => { + try { + const content = await git.readNoteFile(notebookId, noteId); + return { success: true, content }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to read note file', + }; + } + }, }); - // Delete note file from git repository - ipcMain.handle('git:deleteNote', async (_event, notebookId: string, noteId: string) => { - try { - await git.deleteNoteFile(notebookId, noteId); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to delete note file', - }; - } + defineIpcHandler({ + channel: 'git:deleteNote', + args: z.tuple([IdSchema, IdSchema]), + handler: async (notebookId, noteId) => { + try { + await git.deleteNoteFile(notebookId, noteId); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to delete note file', + }; + } + }, }); } diff --git a/apps/desktop/src/main/handlers/licenseHandlers.ts b/apps/desktop/src/main/handlers/licenseHandlers.ts index 5d169c81..479cf0b7 100644 --- a/apps/desktop/src/main/handlers/licenseHandlers.ts +++ b/apps/desktop/src/main/handlers/licenseHandlers.ts @@ -5,7 +5,8 @@ * Fetches subscription status from API with local caching. */ -import { ipcMain, shell } from 'electron'; +import { shell } from 'electron'; +import { z } from 'zod'; import type { LicenseStorage, AppLicenseState, @@ -18,6 +19,7 @@ import { canStartTrial, isCachedSubscriptionValid, } from '@readied/licensing'; +import { defineIpcHandler } from '../ipc/registry.js'; import { loggers } from '../logger'; import type { ApiClient, SubscriptionStatus } from '../services/apiClient'; @@ -116,62 +118,62 @@ async function getSubscriptionData( export function registerLicenseHandlers(deps: LicenseHandlerDependencies): void { const { licenseStorage, apiClient } = deps; - /** - * Get current license state - * Trial data is local, subscription data comes from server (with local cache) - */ - ipcMain.handle('license:getState', async (): Promise => { - let trialData = await licenseStorage.readTrialData(); - const subscriptionData = await getSubscriptionData(licenseStorage, apiClient); - - // Auto-start trial if user hasn't started one yet - if (canStartTrial(trialData, subscriptionData)) { - trialData = startTrial(); - await licenseStorage.writeTrialData(trialData); - getLicenseLogger().info('Trial started automatically'); - } + defineIpcHandler({ + channel: 'license:getState', + args: z.tuple([]), + handler: async (): Promise => { + let trialData = await licenseStorage.readTrialData(); + const subscriptionData = await getSubscriptionData(licenseStorage, apiClient); + + if (canStartTrial(trialData, subscriptionData)) { + trialData = startTrial(); + await licenseStorage.writeTrialData(trialData); + getLicenseLogger().info('Trial started automatically'); + } - return computeLicenseState(trialData, subscriptionData); + return computeLicenseState(trialData, subscriptionData); + }, }); - /** - * Force-refresh subscription status from API (ignores cache) - */ - ipcMain.handle('license:refreshSubscription', async (): Promise => { - const trialData = await licenseStorage.readTrialData(); - const subscriptionData = await getSubscriptionData(licenseStorage, apiClient, true); - return computeLicenseState(trialData, subscriptionData); + defineIpcHandler({ + channel: 'license:refreshSubscription', + args: z.tuple([]), + handler: async (): Promise => { + const trialData = await licenseStorage.readTrialData(); + const subscriptionData = await getSubscriptionData(licenseStorage, apiClient, true); + return computeLicenseState(trialData, subscriptionData); + }, }); - /** - * Start trial manually (if not auto-started) - */ - ipcMain.handle('license:startTrial', async (): Promise<{ success: boolean; error?: string }> => { - const trialData = await licenseStorage.readTrialData(); - const subscriptionData = await licenseStorage.readSubscriptionData(); + defineIpcHandler({ + channel: 'license:startTrial', + args: z.tuple([]), + handler: async (): Promise<{ success: boolean; error?: string }> => { + const trialData = await licenseStorage.readTrialData(); + const subscriptionData = await licenseStorage.readSubscriptionData(); - if (!canStartTrial(trialData, subscriptionData)) { - return { success: false, error: 'Trial already started or subscription active' }; - } + if (!canStartTrial(trialData, subscriptionData)) { + return { success: false, error: 'Trial already started or subscription active' }; + } - const newTrialData = startTrial(); - await licenseStorage.writeTrialData(newTrialData); - getLicenseLogger().info('Trial started manually'); - return { success: true }; + const newTrialData = startTrial(); + await licenseStorage.writeTrialData(newTrialData); + getLicenseLogger().info('Trial started manually'); + return { success: true }; + }, }); - /** - * Open subscription checkout page - * Creates a Stripe checkout session via API and opens it in the browser - */ - ipcMain.handle( - 'license:openSubscribe', - async ( - _event, - options?: { plan?: 'monthly' | 'annual' } - ): Promise<{ success: boolean; error?: string }> => { + defineIpcHandler({ + channel: 'license:openSubscribe', + args: z.tuple([ + z + .object({ + plan: z.enum(['monthly', 'annual']).optional(), + }) + .optional(), + ]), + handler: async (options): Promise<{ success: boolean; error?: string }> => { try { - // Get current user to verify authentication const user = await apiClient.getCurrentUser(); if (!user || !user.email) { @@ -184,7 +186,6 @@ export function registerLicenseHandlers(deps: LicenseHandlerDependencies): void 'Creating checkout session via API' ); - // Create checkout session via API (server handles Stripe SDK) const { url } = await apiClient.createCheckoutSession({ plan: options?.plan || 'monthly', successUrl: 'https://readied.app/subscription/success', @@ -195,7 +196,6 @@ export function registerLicenseHandlers(deps: LicenseHandlerDependencies): void return { success: false, error: 'No checkout URL returned' }; } - // Open checkout URL in browser await shell.openExternal(url); getLicenseLogger().info({ email: user.email }, 'Checkout session opened in browser'); @@ -207,6 +207,6 @@ export function registerLicenseHandlers(deps: LicenseHandlerDependencies): void error: error instanceof Error ? error.message : 'Failed to create checkout session', }; } - } - ); + }, + }); } diff --git a/apps/desktop/src/main/handlers/localServerHandlers.ts b/apps/desktop/src/main/handlers/localServerHandlers.ts index 78435fe7..9e6eb3f3 100644 --- a/apps/desktop/src/main/handlers/localServerHandlers.ts +++ b/apps/desktop/src/main/handlers/localServerHandlers.ts @@ -5,13 +5,15 @@ * to the renderer (settings UI). */ -import { ipcMain, app } from 'electron'; +import { app } from 'electron'; +import { z } from 'zod'; import { createNoteId, createNoteOperation, updateNoteOperation } from '@readied/core'; import { LocalServer, getOrCreateApiToken, type LocalServerHandlers, } from '../services/localServer.js'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { SQLiteNoteRepository, DataPaths } from './types.js'; // ============================================================================ @@ -131,49 +133,56 @@ export function registerLocalServerHandlers(deps: LocalServerHandlerDeps): void }, }; - // IPC: Start the local server - ipcMain.handle('localServer:start', async (_event, port?: number) => { - try { - if (port !== undefined && (typeof port !== 'number' || port < 1 || port > 65535)) { - return { ok: false, error: 'Invalid port' }; + defineIpcHandler({ + channel: 'localServer:start', + args: z.tuple([z.number().int().min(1).max(65535).optional()]), + handler: async port => { + try { + if (server.isRunning()) return { ok: true, port: server.getPort() }; + apiToken = await getOrCreateApiToken(dataPaths.root); + await server.start(port, apiToken, handlers); + return { ok: true, port: server.getPort() }; + } catch (err) { + return { ok: false, error: err instanceof Error ? err.message : String(err) }; } - if (server.isRunning()) return { ok: true, port: server.getPort() }; - apiToken = await getOrCreateApiToken(dataPaths.root); - await server.start(port, apiToken, handlers); - return { ok: true, port: server.getPort() }; - } catch (err) { - return { ok: false, error: err instanceof Error ? err.message : String(err) }; - } + }, }); - // IPC: Stop the local server - ipcMain.handle('localServer:stop', async () => { - try { - await server.stop(); - return { ok: true }; - } catch (err) { - return { ok: false, error: err instanceof Error ? err.message : String(err) }; - } + defineIpcHandler({ + channel: 'localServer:stop', + args: z.tuple([]), + handler: async () => { + try { + await server.stop(); + return { ok: true }; + } catch (err) { + return { ok: false, error: err instanceof Error ? err.message : String(err) }; + } + }, }); - // IPC: Get server status - ipcMain.handle('localServer:status', () => { - return { + defineIpcHandler({ + channel: 'localServer:status', + args: z.tuple([]), + handler: () => ({ running: server.isRunning(), port: server.getPort(), - }; + }), }); - // IPC: Get the bearer token (for displaying in settings) - ipcMain.handle('localServer:getToken', async () => { - try { - if (!apiToken) { - apiToken = await getOrCreateApiToken(dataPaths.root); + defineIpcHandler({ + channel: 'localServer:getToken', + args: z.tuple([]), + handler: async () => { + try { + if (!apiToken) { + apiToken = await getOrCreateApiToken(dataPaths.root); + } + return { ok: true, value: apiToken }; + } catch (err) { + return { ok: false, error: err instanceof Error ? err.message : String(err) }; } - return { ok: true, value: apiToken }; - } catch (err) { - return { ok: false, error: err instanceof Error ? err.message : String(err) }; - } + }, }); } diff --git a/apps/desktop/src/main/handlers/logHandlers.ts b/apps/desktop/src/main/handlers/logHandlers.ts index 8e478822..c629706e 100644 --- a/apps/desktop/src/main/handlers/logHandlers.ts +++ b/apps/desktop/src/main/handlers/logHandlers.ts @@ -1,11 +1,14 @@ /** * Log IPC Handlers * - * Handles renderer-side logging via IPC. + * Handles renderer-side logging via IPC. Validated at the boundary: + * level must be one of the enum values; message capped at 16 KiB; + * context object size is left to JSON serialization limits. */ -import { ipcMain } from 'electron'; -import { createChildLogger, type LogLevel } from '../logger'; +import { z } from 'zod'; +import { defineIpcHandler } from '../ipc/registry.js'; +import { createChildLogger } from '../logger'; import type { DataPaths } from './types.js'; export interface LogHandlerDeps { @@ -13,20 +16,18 @@ export interface LogHandlerDeps { getDataPaths: () => DataPaths | null; } +const LogLevelSchema = z.enum(['debug', 'info', 'warn', 'error']); +const LogMessageSchema = z.string().max(16384); +const LogContextSchema = z.record(z.string(), z.unknown()).optional(); + export function registerLogHandlers(deps: LogHandlerDeps): void { const rendererLogger = createChildLogger({ component: 'renderer' }); - // Log from renderer - ipcMain.handle( - 'log:write', - async ( - _event, - level: LogLevel, - message: string, - context?: Record - ): Promise<{ success: boolean }> => { + defineIpcHandler({ + channel: 'log:write', + args: z.tuple([LogLevelSchema, LogMessageSchema, LogContextSchema]), + handler: (level, message, context): { success: boolean } => { const childLogger = context ? rendererLogger.child(context) : rendererLogger; - switch (level) { case 'debug': childLogger.debug(message); @@ -41,13 +42,13 @@ export function registerLogHandlers(deps: LogHandlerDeps): void { childLogger.error(message); break; } - return { success: true }; - } - ); + }, + }); - // Get log file path (for debugging/support) - ipcMain.handle('log:getPath', async (): Promise => { - return deps.getDataPaths()?.logs ?? null; + defineIpcHandler({ + channel: 'log:getPath', + args: z.tuple([]), + handler: (): string | null => deps.getDataPaths()?.logs ?? null, }); } diff --git a/apps/desktop/src/main/handlers/noteHandlers.ts b/apps/desktop/src/main/handlers/noteHandlers.ts index 329c1927..e1412c89 100644 --- a/apps/desktop/src/main/handlers/noteHandlers.ts +++ b/apps/desktop/src/main/handlers/noteHandlers.ts @@ -7,7 +7,7 @@ import { join } from 'path'; import { existsSync } from 'fs'; import { mkdir, writeFile } from 'fs/promises'; -import { ipcMain } from 'electron'; +import { z } from 'zod'; import { createNoteOperation, updateNoteOperation, @@ -26,6 +26,7 @@ import { type NoteStatus, } from '@readied/core'; import { createNoteId, createNotebookId, createTag } from '@readied/core'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { SQLiteNoteRepository, DataPaths, NoteToSnapshotFn } from './types.js'; export interface NoteHandlerDeps { @@ -34,298 +35,327 @@ export interface NoteHandlerDeps { noteToSnapshot: NoteToSnapshotFn; } +const IdSchema = z.string().min(1).max(128); +const TitleSchema = z.string().max(512); +const ContentSchema = z.string().max(10 * 1024 * 1024); // 10 MiB cap on note content +const TagSchema = z.string().min(1).max(64); +const StatusSchema: z.ZodType = z.enum(['active', 'on_hold', 'completed', 'dropped']); + export function registerNoteHandlers(deps: NoteHandlerDeps): void { const { noteRepository: repo, dataPaths, noteToSnapshot } = deps; - // Create note - ipcMain.handle( - 'notes:create', - async (_event, input: { content: string; id?: string; notebookId?: string }) => { - return createNoteOperation(input, repo); - } - ); - - // Get note - ipcMain.handle('notes:get', async (_event, id: string) => { - const noteId = createNoteId(id); - return getNoteOperation({ id: noteId }, repo); + // ── Notes CRUD ────────────────────────────────────────────────────────── + + defineIpcHandler({ + channel: 'notes:create', + args: z.tuple([ + z.object({ + content: ContentSchema, + id: IdSchema.optional(), + notebookId: IdSchema.optional(), + }), + ]), + handler: input => createNoteOperation(input, repo), }); - // Update note content - ipcMain.handle('notes:update', async (_event, input: { id: string; content: string }) => { - const noteId = createNoteId(input.id); - return updateNoteOperation({ id: noteId, content: input.content }, repo); + defineIpcHandler({ + channel: 'notes:get', + args: z.tuple([IdSchema]), + handler: id => getNoteOperation({ id: createNoteId(id) }, repo), }); - // Update note title (structural, independent from content) - ipcMain.handle('notes:updateTitle', async (_event, input: { id: string; title: string }) => { - const noteId = createNoteId(input.id); - return updateTitleOperation({ id: noteId, title: input.title }, repo); + defineIpcHandler({ + channel: 'notes:update', + args: z.tuple([z.object({ id: IdSchema, content: ContentSchema })]), + handler: input => + updateNoteOperation({ id: createNoteId(input.id), content: input.content }, repo), }); - // Delete note - ipcMain.handle('notes:delete', async (_event, id: string) => { - const noteId = createNoteId(id); - return deleteNoteOperation({ id: noteId }, repo); + defineIpcHandler({ + channel: 'notes:updateTitle', + args: z.tuple([z.object({ id: IdSchema, title: TitleSchema })]), + handler: input => + updateTitleOperation({ id: createNoteId(input.id), title: input.title }, repo), }); - // Archive note - ipcMain.handle('notes:archive', async (_event, id: string) => { - const noteId = createNoteId(id); - return archiveNoteOperation({ id: noteId }, repo); + defineIpcHandler({ + channel: 'notes:delete', + args: z.tuple([IdSchema]), + handler: id => deleteNoteOperation({ id: createNoteId(id) }, repo), }); - // Restore note - ipcMain.handle('notes:restore', async (_event, id: string) => { - const noteId = createNoteId(id); - return restoreNoteOperation({ id: noteId }, repo); + defineIpcHandler({ + channel: 'notes:archive', + args: z.tuple([IdSchema]), + handler: id => archiveNoteOperation({ id: createNoteId(id) }, repo), }); - // Duplicate note - ipcMain.handle('notes:duplicate', async (_event, id: string) => { - const noteId = createNoteId(id); - return duplicateNoteOperation({ id: noteId }, repo); + defineIpcHandler({ + channel: 'notes:restore', + args: z.tuple([IdSchema]), + handler: id => restoreNoteOperation({ id: createNoteId(id) }, repo), }); - // Move note to notebook - ipcMain.handle('notes:move', async (_event, noteId: string, notebookId: string) => { - const note = await repo.get(createNoteId(noteId)); - if (!note) { - return { ok: false, error: { type: 'NOT_FOUND', id: noteId } }; - } - - const movedNote = moveNoteToNotebook(note, createNotebookId(notebookId)); - await repo.save(movedNote); - - return { - ok: true, - data: noteToSnapshot(movedNote), - }; + defineIpcHandler({ + channel: 'notes:duplicate', + args: z.tuple([IdSchema]), + handler: id => duplicateNoteOperation({ id: createNoteId(id) }, repo), }); - // Pin note - ipcMain.handle('notes:pin', async (_event, id: string) => { - const note = await repo.get(createNoteId(id)); - if (!note) { - return { ok: false, error: { type: 'NOT_FOUND', id } }; - } - - const pinnedNote = pinNote(note); - await repo.save(pinnedNote); - - return { ok: true, data: noteToSnapshot(pinnedNote) }; + defineIpcHandler({ + channel: 'notes:move', + args: z.tuple([IdSchema, IdSchema]), + handler: async (noteId, notebookId) => { + const note = await repo.get(createNoteId(noteId)); + if (!note) { + return { ok: false, error: { type: 'NOT_FOUND', id: noteId } }; + } + const movedNote = moveNoteToNotebook(note, createNotebookId(notebookId)); + await repo.save(movedNote); + return { ok: true, data: noteToSnapshot(movedNote) }; + }, }); - // Unpin note - ipcMain.handle('notes:unpin', async (_event, id: string) => { - const note = await repo.get(createNoteId(id)); - if (!note) { - return { ok: false, error: { type: 'NOT_FOUND', id } }; - } - - const unpinnedNote = unpinNote(note); - await repo.save(unpinnedNote); - - return { ok: true, data: noteToSnapshot(unpinnedNote) }; + defineIpcHandler({ + channel: 'notes:pin', + args: z.tuple([IdSchema]), + handler: async id => { + const note = await repo.get(createNoteId(id)); + if (!note) return { ok: false, error: { type: 'NOT_FOUND', id } }; + const pinnedNote = pinNote(note); + await repo.save(pinnedNote); + return { ok: true, data: noteToSnapshot(pinnedNote) }; + }, }); - // Soft delete (move to trash) - ipcMain.handle('notes:softDelete', async (_event, id: string) => { - const note = await repo.get(createNoteId(id)); - if (!note) { - return { ok: false, error: { type: 'NOT_FOUND', id } }; - } - - const deletedNote = softDeleteNote(note); - await repo.save(deletedNote); - - return { ok: true, data: noteToSnapshot(deletedNote) }; + defineIpcHandler({ + channel: 'notes:unpin', + args: z.tuple([IdSchema]), + handler: async id => { + const note = await repo.get(createNoteId(id)); + if (!note) return { ok: false, error: { type: 'NOT_FOUND', id } }; + const unpinnedNote = unpinNote(note); + await repo.save(unpinnedNote); + return { ok: true, data: noteToSnapshot(unpinnedNote) }; + }, }); - // Restore from trash - ipcMain.handle('notes:restoreDeleted', async (_event, id: string) => { - const note = await repo.get(createNoteId(id)); - if (!note) { - return { ok: false, error: { type: 'NOT_FOUND', id } }; - } - - const restoredNote = restoreDeletedNote(note); - await repo.save(restoredNote); - - return { ok: true, data: noteToSnapshot(restoredNote) }; + defineIpcHandler({ + channel: 'notes:softDelete', + args: z.tuple([IdSchema]), + handler: async id => { + const note = await repo.get(createNoteId(id)); + if (!note) return { ok: false, error: { type: 'NOT_FOUND', id } }; + const deletedNote = softDeleteNote(note); + await repo.save(deletedNote); + return { ok: true, data: noteToSnapshot(deletedNote) }; + }, }); - // Set note status - ipcMain.handle('notes:setStatus', async (_event, id: string, status: NoteStatus) => { - const note = await repo.get(createNoteId(id)); - if (!note) { - return { ok: false, error: { type: 'NOT_FOUND', id } }; - } - - const updatedNote = setNoteStatus(note, status); - await repo.save(updatedNote); + defineIpcHandler({ + channel: 'notes:restoreDeleted', + args: z.tuple([IdSchema]), + handler: async id => { + const note = await repo.get(createNoteId(id)); + if (!note) return { ok: false, error: { type: 'NOT_FOUND', id } }; + const restoredNote = restoreDeletedNote(note); + await repo.save(restoredNote); + return { ok: true, data: noteToSnapshot(restoredNote) }; + }, + }); - return { ok: true, data: noteToSnapshot(updatedNote) }; + defineIpcHandler({ + channel: 'notes:setStatus', + args: z.tuple([IdSchema, StatusSchema]), + handler: async (id, status) => { + const note = await repo.get(createNoteId(id)); + if (!note) return { ok: false, error: { type: 'NOT_FOUND', id } }; + const updatedNote = setNoteStatus(note, status); + await repo.save(updatedNote); + return { ok: true, data: noteToSnapshot(updatedNote) }; + }, }); - // List notes - ipcMain.handle( - 'notes:list', - async ( - _event, - options?: { - limit?: number; - offset?: number; - tag?: string; - sortBy?: 'createdAt' | 'updatedAt' | 'title'; - sortOrder?: 'asc' | 'desc'; - archived?: 'active' | 'archived' | 'all'; - } - ) => { + defineIpcHandler({ + channel: 'notes:list', + args: z.tuple([ + z + .object({ + limit: z.number().int().positive().max(100000).optional(), + offset: z.number().int().nonnegative().optional(), + tag: TagSchema.optional(), + sortBy: z.enum(['createdAt', 'updatedAt', 'title']).optional(), + sortOrder: z.enum(['asc', 'desc']).optional(), + archived: z.enum(['active', 'archived', 'all']).optional(), + }) + .optional(), + ]), + handler: async options => { const notes = await repo.list(options); return notes.map(note => noteToSnapshot(note)); - } - ); + }, + }); - // Search notes - ipcMain.handle('notes:search', async (_event, query: string, limit?: number) => { - const notes = await repo.search(query, limit); - return notes.map(note => noteToSnapshot(note)); + defineIpcHandler({ + channel: 'notes:search', + args: z.tuple([z.string().max(2048), z.number().int().positive().max(10000).optional()]), + handler: async (query, limit) => { + const notes = await repo.search(query, limit); + return notes.map(note => noteToSnapshot(note)); + }, }); - // Get all tags - ipcMain.handle('notes:tags', async () => { - return repo.getAllTags(); + // ── Tags ──────────────────────────────────────────────────────────────── + + defineIpcHandler({ + channel: 'notes:tags', + args: z.tuple([]), + handler: () => repo.getAllTags(), }); - // Set manual tags (full replacement) - ipcMain.handle('notes:setManualTags', async (_event, noteId: string, tags: string[]) => { - const id = createNoteId(noteId); - // Normalize tags: trim, lowercase, strip leading '#', remove empties, dedupe - const normalizedTags = [ - ...new Set(tags.map(t => t.trim().toLowerCase().replace(/^#/, '')).filter(t => t.length > 0)), - ]; - repo.setManualTags( - id, - normalizedTags.map(t => createTag(t)) - ); - return { ok: true }; + defineIpcHandler({ + channel: 'notes:setManualTags', + args: z.tuple([IdSchema, z.array(TagSchema).max(256)]), + handler: (noteId, tags) => { + const id = createNoteId(noteId); + const normalizedTags = [ + ...new Set( + tags.map(t => t.trim().toLowerCase().replace(/^#/, '')).filter(t => t.length > 0) + ), + ]; + repo.setManualTags( + id, + normalizedTags.map(t => createTag(t)) + ); + return { ok: true }; + }, }); - // Get manual tags only (for editor to know which are removable) - ipcMain.handle('notes:getManualTags', async (_event, noteId: string) => { - const id = createNoteId(noteId); - return repo.getManualTags(id); + defineIpcHandler({ + channel: 'notes:getManualTags', + args: z.tuple([IdSchema]), + handler: noteId => repo.getManualTags(createNoteId(noteId)), }); - // Get all tags with colors - ipcMain.handle('tags:listWithColors', async () => { - return repo.getAllTagsWithColors(); + defineIpcHandler({ + channel: 'tags:listWithColors', + args: z.tuple([]), + handler: () => repo.getAllTagsWithColors(), }); - // Set tag color - ipcMain.handle('tags:setColor', async (_event, tagName: string, color: string | null) => { - repo.setTagColor(tagName, color); - return { ok: true }; + defineIpcHandler({ + channel: 'tags:setColor', + args: z.tuple([TagSchema, z.string().max(32).nullable()]), + handler: (tagName, color) => { + repo.setTagColor(tagName, color); + return { ok: true }; + }, }); - // Delete tag from system - ipcMain.handle('tags:delete', async (_event, tagName: string) => { - repo.deleteTag(tagName); - return { ok: true }; + defineIpcHandler({ + channel: 'tags:delete', + args: z.tuple([TagSchema]), + handler: tagName => { + repo.deleteTag(tagName); + return { ok: true }; + }, }); - // Rename tag across all notes - ipcMain.handle('tags:rename', async (_event, oldName: string, newName: string) => { - return repo.renameTag(oldName, newName); + defineIpcHandler({ + channel: 'tags:rename', + args: z.tuple([TagSchema, TagSchema]), + handler: (oldName, newName) => repo.renameTag(oldName, newName), }); - // ═══════════════════════════════════════════════════════════════════════════ - // Links (Wikilinks / Backlinks) - // ═══════════════════════════════════════════════════════════════════════════ + // ── Links (Wikilinks / Backlinks) ─────────────────────────────────────── - // Sync links for a note (call after saving note) - ipcMain.handle('links:sync', async (_event, noteId: string, content: string) => { - repo.syncLinks(createNoteId(noteId), content); - return { ok: true }; + defineIpcHandler({ + channel: 'links:sync', + args: z.tuple([IdSchema, ContentSchema]), + handler: (noteId, content) => { + repo.syncLinks(createNoteId(noteId), content); + return { ok: true }; + }, }); - // Get backlinks (notes that link TO this note) - ipcMain.handle('links:backlinks', async (_event, noteId: string) => { - return repo.getBacklinks(createNoteId(noteId)); + defineIpcHandler({ + channel: 'links:backlinks', + args: z.tuple([IdSchema]), + handler: noteId => repo.getBacklinks(createNoteId(noteId)), }); - // Get outgoing links (notes this note links TO) - ipcMain.handle('links:outgoing', async (_event, noteId: string) => { - return repo.getOutgoingLinks(createNoteId(noteId)); + defineIpcHandler({ + channel: 'links:outgoing', + args: z.tuple([IdSchema]), + handler: noteId => repo.getOutgoingLinks(createNoteId(noteId)), }); - // Get graph data (all notes and links for visualization) - ipcMain.handle('links:graph', async () => { - try { - return repo.getGraphData(); - } catch (error) { - console.error('Failed to get graph data:', error); - // Return empty data on error - return { nodes: [], edges: [] }; - } + defineIpcHandler({ + channel: 'links:graph', + args: z.tuple([]), + handler: () => { + try { + return repo.getGraphData(); + } catch (error) { + console.error('Failed to get graph data:', error); + return { nodes: [], edges: [] }; + } + }, }); - // ═══════════════════════════════════════════════════════════════════════════ - // Embeds (File Resolution) - // ═══════════════════════════════════════════════════════════════════════════ - - // Resolve embed target to asset:// URL - ipcMain.handle('embeds:resolve', async (_event, target: string, noteId: string) => { - // Build path to note's assets folder: /assets/{noteId}/{target} - const assetPath = join(dataPaths.assets, noteId, target); - - // Check if file exists - if (existsSync(assetPath)) { - // Return asset:// URL with host (required for browser to recognize protocol) - return `asset://local/${noteId}/${target}`; - } - - // File not found - return null; + // ── Embeds (File Resolution) ──────────────────────────────────────────── + + // Embed targets are filenames inside an asset folder — restrict to + // characters that can appear in a generated asset name (no slashes, no + // path traversal). The relative-path check inside `join` would catch + // most issues but rejecting at the boundary is cheaper. + const EmbedTargetSchema = z + .string() + .min(1) + .max(256) + .regex(/^[a-zA-Z0-9._-]+$/); + + defineIpcHandler({ + channel: 'embeds:resolve', + args: z.tuple([EmbedTargetSchema, IdSchema]), + handler: (target, noteId) => { + const assetPath = join(dataPaths.assets, noteId, target); + return existsSync(assetPath) ? `asset://local/${noteId}/${target}` : null; + }, }); - // Batch resolve multiple embed targets (more efficient) - ipcMain.handle( - 'embeds:resolveBatch', - async (_event, targets: string[], noteId: string): Promise> => { + defineIpcHandler({ + channel: 'embeds:resolveBatch', + args: z.tuple([z.array(EmbedTargetSchema).max(1000), IdSchema]), + handler: (targets, noteId): Record => { const result: Record = {}; for (const target of targets) { const assetPath = join(dataPaths.assets, noteId, target); - // Return asset:// URL with host (required for browser to recognize protocol) result[target] = existsSync(assetPath) ? `asset://local/${noteId}/${target}` : null; } return result; - } - ); - - // Save asset (image/file) for a note via drag & drop or paste - ipcMain.handle( - 'embeds:saveAsset', - async ( - _event, - noteId: string, - mime: string, - bytes: ArrayBuffer, - originalName?: string - ): Promise<{ ok: true; filename: string; relPath: string } | { ok: false; error: string }> => { - // Validate noteId (non-empty, alphanumeric with hyphens/underscores) - if (!noteId || !/^[\w-]+$/.test(noteId)) { - return { ok: false, error: 'Invalid noteId' }; - } + }, + }); - // Validate size (max 20MB) + defineIpcHandler({ + channel: 'embeds:saveAsset', + args: z.tuple([ + IdSchema.regex(/^[\w-]+$/), + z.string().min(1).max(256), + z.instanceof(ArrayBuffer), + z.string().max(512).optional(), + ]), + handler: async ( + noteId, + mime, + bytes, + originalName + ): Promise<{ ok: true; filename: string; relPath: string } | { ok: false; error: string }> => { const MAX_SIZE = 20 * 1024 * 1024; if (bytes.byteLength > MAX_SIZE) { return { ok: false, error: 'File too large (max 20MB)' }; } - // Derive extension from mime type const mimeToExt: Record = { 'image/png': 'png', 'image/jpeg': 'jpg', @@ -346,142 +376,122 @@ export function registerNoteHandlers(deps: NoteHandlerDeps): void { let ext = mimeToExt[mime]; if (!ext && originalName) { - // Fallback to originalName extension const match = originalName.match(/\.([a-zA-Z0-9]+)$/); ext = match?.[1]?.toLowerCase() ?? 'bin'; } - if (!ext) { - ext = 'bin'; - } + if (!ext) ext = 'bin'; - // Generate unique filename: timestamp-random.ext const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); const filename = `${timestamp}-${random}.${ext}`; - // Ensure note's assets directory exists const noteAssetsDir = join(dataPaths.assets, noteId); await mkdir(noteAssetsDir, { recursive: true }); - // Write file const assetPath = join(noteAssetsDir, filename); await writeFile(assetPath, Buffer.from(bytes)); - return { - ok: true, - filename, - relPath: `${noteId}/${filename}`, - }; - } - ); - - // Activity stats (notes created/updated per week, last 52 weeks) - ipcMain.handle('notes:activityStats', async () => { - const allNotes = await repo.list({ archived: 'all', limit: 10000 }); - const now = Date.now(); - const fiftyTwoWeeksAgo = now - 52 * 7 * 24 * 60 * 60 * 1000; - - // Build a map of week -> { created, updated } - const weekMap = new Map(); - - for (const note of allNotes) { - const createdMs = new Date(note.metadata.createdAt).getTime(); - const updatedMs = new Date(note.metadata.updatedAt).getTime(); - - if (createdMs >= fiftyTwoWeeksAgo) { - const weekKey = getISOWeek(new Date(note.metadata.createdAt)); - const entry = weekMap.get(weekKey) ?? { created: 0, updated: 0 }; - entry.created++; - weekMap.set(weekKey, entry); - } + return { ok: true, filename, relPath: `${noteId}/${filename}` }; + }, + }); - if (updatedMs >= fiftyTwoWeeksAgo && updatedMs !== createdMs) { - const weekKey = getISOWeek(new Date(note.metadata.updatedAt)); - const entry = weekMap.get(weekKey) ?? { created: 0, updated: 0 }; - entry.updated++; - weekMap.set(weekKey, entry); + // ── Stats / counts ────────────────────────────────────────────────────── + + defineIpcHandler({ + channel: 'notes:activityStats', + args: z.tuple([]), + handler: async () => { + const allNotes = await repo.list({ archived: 'all', limit: 10000 }); + const now = Date.now(); + const fiftyTwoWeeksAgo = now - 52 * 7 * 24 * 60 * 60 * 1000; + + const weekMap = new Map(); + + for (const note of allNotes) { + const createdMs = new Date(note.metadata.createdAt).getTime(); + const updatedMs = new Date(note.metadata.updatedAt).getTime(); + + if (createdMs >= fiftyTwoWeeksAgo) { + const weekKey = getISOWeek(new Date(note.metadata.createdAt)); + const entry = weekMap.get(weekKey) ?? { created: 0, updated: 0 }; + entry.created++; + weekMap.set(weekKey, entry); + } + + if (updatedMs >= fiftyTwoWeeksAgo && updatedMs !== createdMs) { + const weekKey = getISOWeek(new Date(note.metadata.updatedAt)); + const entry = weekMap.get(weekKey) ?? { created: 0, updated: 0 }; + entry.updated++; + weekMap.set(weekKey, entry); + } } - } - - // Convert to sorted array - const weeks = Array.from(weekMap.entries()) - .map(([week, counts]) => ({ week, ...counts })) - .sort((a, b) => a.week.localeCompare(b.week)); - - // Calculate current streak (consecutive weeks with activity ending at current week) - const currentWeek = getISOWeek(new Date()); - let streak = 0; - let checkDate = new Date(); - for (let i = 0; i < 52; i++) { - const weekKey = getISOWeek(checkDate); - const entry = weekMap.get(weekKey); - if (entry && (entry.created > 0 || entry.updated > 0)) { - streak++; - } else if (i > 0) { - // Allow current week to have no activity yet - break; + + const weeks = Array.from(weekMap.entries()) + .map(([week, counts]) => ({ week, ...counts })) + .sort((a, b) => a.week.localeCompare(b.week)); + + const currentWeek = getISOWeek(new Date()); + let streak = 0; + let checkDate = new Date(); + for (let i = 0; i < 52; i++) { + const weekKey = getISOWeek(checkDate); + const entry = weekMap.get(weekKey); + if (entry && (entry.created > 0 || entry.updated > 0)) { + streak++; + } else if (i > 0) { + break; + } + checkDate = new Date(checkDate.getTime() - 7 * 24 * 60 * 60 * 1000); } - checkDate = new Date(checkDate.getTime() - 7 * 24 * 60 * 60 * 1000); - } - - return { - weeks, - totalNotes: allNotes.length, - currentStreak: streak, - currentWeek, - }; - }); - - // Count notes - ipcMain.handle('notes:count', async () => { - // Get all notes to compute counts - const allNotes = await repo.list({ archived: 'all' }); - - const counts = { - active: 0, - archived: 0, - total: allNotes.length, - pinned: 0, - deleted: 0, - byStatus: { + + return { + weeks, + totalNotes: allNotes.length, + currentStreak: streak, + currentWeek, + }; + }, + }); + + defineIpcHandler({ + channel: 'notes:count', + args: z.tuple([]), + handler: async () => { + const allNotes = await repo.list({ archived: 'all' }); + + const counts = { active: 0, - on_hold: 0, - completed: 0, - dropped: 0, - } as Record, - byNotebook: {} as Record, - }; - - for (const note of allNotes) { - // Count archived - if (note.metadata.archivedAt !== null) { - counts.archived++; - } else { - counts.active++; - } + archived: 0, + total: allNotes.length, + pinned: 0, + deleted: 0, + byStatus: { + active: 0, + on_hold: 0, + completed: 0, + dropped: 0, + } as Record, + byNotebook: {} as Record, + }; - // Count pinned - if (note.isPinned) { - counts.pinned++; - } + for (const note of allNotes) { + if (note.metadata.archivedAt !== null) counts.archived++; + else counts.active++; - // Count deleted (in trash) - if (note.isDeleted) { - counts.deleted++; - } + if (note.isPinned) counts.pinned++; + if (note.isDeleted) counts.deleted++; - // Count by status - if (note.status && counts.byStatus[note.status] !== undefined) { - counts.byStatus[note.status]++; - } + if (note.status && counts.byStatus[note.status] !== undefined) { + counts.byStatus[note.status]++; + } - // Count by notebook (active, non-deleted notes only) - if (note.notebookId && !note.isDeleted && !note.metadata.archivedAt) { - counts.byNotebook[note.notebookId] = (counts.byNotebook[note.notebookId] || 0) + 1; + if (note.notebookId && !note.isDeleted && !note.metadata.archivedAt) { + counts.byNotebook[note.notebookId] = (counts.byNotebook[note.notebookId] || 0) + 1; + } } - } - return counts; + return counts; + }, }); } @@ -489,7 +499,6 @@ export function registerNoteHandlers(deps: NoteHandlerDeps): void { function getISOWeek(date: Date): string { const d = new Date(date.getTime()); d.setHours(0, 0, 0, 0); - // Set to nearest Thursday (current date + 4 - current day number, with Sunday=7) d.setDate(d.getDate() + 4 - (d.getDay() || 7)); const yearStart = new Date(d.getFullYear(), 0, 1); const weekNum = Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7); diff --git a/apps/desktop/src/main/handlers/notebookHandlers.ts b/apps/desktop/src/main/handlers/notebookHandlers.ts index e72524d3..92835658 100644 --- a/apps/desktop/src/main/handlers/notebookHandlers.ts +++ b/apps/desktop/src/main/handlers/notebookHandlers.ts @@ -4,7 +4,7 @@ * Handles notebook CRUD, git settings per notebook, and reordering. */ -import { ipcMain } from 'electron'; +import { z } from 'zod'; import { createNotebookId, createNotebook, @@ -12,267 +12,271 @@ import { moveNotebook, INBOX_NOTEBOOK_ID, } from '@readied/core'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { SQLiteNotebookRepository } from './types.js'; export interface NotebookHandlerDeps { notebookRepository: SQLiteNotebookRepository; } +const IdSchema = z.string().min(1).max(128); +const NameSchema = z.string().min(1).max(256); + export function registerNotebookHandlers(deps: NotebookHandlerDeps): void { const { notebookRepository: repo } = deps; - // List all notebooks - ipcMain.handle('notebooks:list', async () => { - const notebooks = await repo.getAll(); - return notebooks.map(nb => ({ - id: nb.id, - name: nb.name, - parentId: nb.parentId, - depth: nb.depth, - order: nb.order, - createdAt: nb.createdAt, - updatedAt: nb.updatedAt, - })); + const serialize = (nb: { + id: string; + name: string; + parentId: string | null; + depth: number; + order: number; + createdAt: string; + updatedAt: string; + }) => ({ + id: nb.id, + name: nb.name, + parentId: nb.parentId, + depth: nb.depth, + order: nb.order, + createdAt: nb.createdAt, + updatedAt: nb.updatedAt, }); - // Get notebook tree - ipcMain.handle('notebooks:tree', async () => { - return repo.getTree(); + defineIpcHandler({ + channel: 'notebooks:list', + args: z.tuple([]), + handler: async () => { + const notebooks = await repo.getAll(); + return notebooks.map(serialize); + }, }); - // Get single notebook - ipcMain.handle('notebooks:get', async (_event, id: string) => { - const notebook = await repo.get(createNotebookId(id)); - if (!notebook) return null; - return { - id: notebook.id, - name: notebook.name, - parentId: notebook.parentId, - depth: notebook.depth, - order: notebook.order, - createdAt: notebook.createdAt, - updatedAt: notebook.updatedAt, - }; + defineIpcHandler({ + channel: 'notebooks:tree', + args: z.tuple([]), + handler: () => repo.getTree(), }); - // Get notebook with metadata - ipcMain.handle('notebooks:getWithMetadata', async (_event, id: string) => { - const notebook = await repo.getWithMetadata(createNotebookId(id)); - if (!notebook) return null; - return { - id: notebook.id, - name: notebook.name, - parentId: notebook.parentId, - depth: notebook.depth, - order: notebook.order, - createdAt: notebook.createdAt, - updatedAt: notebook.updatedAt, - noteCount: notebook.noteCount, - childCount: notebook.childCount, - }; + defineIpcHandler({ + channel: 'notebooks:get', + args: z.tuple([IdSchema]), + handler: async id => { + const notebook = await repo.get(createNotebookId(id)); + return notebook ? serialize(notebook) : null; + }, }); - // Create notebook - ipcMain.handle('notebooks:create', async (_event, input: { name: string; parentId?: string }) => { - let parentDepth = 0; - if (input.parentId) { - const parent = await repo.get(createNotebookId(input.parentId)); - if (parent) { - parentDepth = parent.depth; - } - } + defineIpcHandler({ + channel: 'notebooks:getWithMetadata', + args: z.tuple([IdSchema]), + handler: async id => { + const notebook = await repo.getWithMetadata(createNotebookId(id)); + if (!notebook) return null; + return { + ...serialize(notebook), + noteCount: notebook.noteCount, + childCount: notebook.childCount, + }; + }, + }); - const nextOrder = await repo.getNextOrder( - input.parentId ? createNotebookId(input.parentId) : null - ); + defineIpcHandler({ + channel: 'notebooks:create', + args: z.tuple([ + z.object({ + name: NameSchema, + parentId: IdSchema.optional(), + }), + ]), + handler: async input => { + let parentDepth = 0; + if (input.parentId) { + const parent = await repo.get(createNotebookId(input.parentId)); + if (parent) parentDepth = parent.depth; + } - const notebook = createNotebook({ - name: input.name, - parentId: input.parentId ? createNotebookId(input.parentId) : null, - parentDepth, - order: nextOrder, - }); + const nextOrder = await repo.getNextOrder( + input.parentId ? createNotebookId(input.parentId) : null + ); - await repo.save(notebook); + const notebook = createNotebook({ + name: input.name, + parentId: input.parentId ? createNotebookId(input.parentId) : null, + parentDepth, + order: nextOrder, + }); - return { - id: notebook.id, - name: notebook.name, - parentId: notebook.parentId, - depth: notebook.depth, - order: notebook.order, - createdAt: notebook.createdAt, - updatedAt: notebook.updatedAt, - }; + await repo.save(notebook); + return serialize(notebook); + }, }); - // Rename notebook - ipcMain.handle('notebooks:rename', async (_event, id: string, name: string) => { - const notebook = await repo.get(createNotebookId(id)); - if (!notebook) { - throw new Error('Notebook not found'); - } - - const updated = renameNotebook(notebook, name); - await repo.save(updated); - - return { - id: updated.id, - name: updated.name, - parentId: updated.parentId, - depth: updated.depth, - order: updated.order, - createdAt: updated.createdAt, - updatedAt: updated.updatedAt, - }; + defineIpcHandler({ + channel: 'notebooks:rename', + args: z.tuple([IdSchema, NameSchema]), + handler: async (id, name) => { + const notebook = await repo.get(createNotebookId(id)); + if (!notebook) { + throw new Error('Notebook not found'); + } + const updated = renameNotebook(notebook, name); + await repo.save(updated); + return serialize(updated); + }, }); - // Move notebook (recursively updates children's depth) - ipcMain.handle('notebooks:move', async (_event, id: string, newParentId: string | null) => { - const notebook = await repo.get(createNotebookId(id)); - if (!notebook) { - throw new Error('Notebook not found'); - } + defineIpcHandler({ + channel: 'notebooks:move', + args: z.tuple([IdSchema, IdSchema.nullable()]), + handler: async (id, newParentId) => { + const notebook = await repo.get(createNotebookId(id)); + if (!notebook) { + throw new Error('Notebook not found'); + } - // Prevent circular reference: can't move a notebook into its own descendant - if (newParentId) { - let current = await repo.get(createNotebookId(newParentId)); - while (current && current.parentId) { - if (current.parentId === notebook.id) { - throw new Error('CIRCULAR_REFERENCE'); + // Prevent circular reference: can't move a notebook into its own descendant + if (newParentId) { + let current = await repo.get(createNotebookId(newParentId)); + while (current && current.parentId) { + if (current.parentId === notebook.id) { + throw new Error('CIRCULAR_REFERENCE'); + } + current = await repo.get(current.parentId); } - current = await repo.get(current.parentId); } - } - let newParentDepth = 0; - if (newParentId) { - const parent = await repo.get(createNotebookId(newParentId)); - if (parent) { - newParentDepth = parent.depth; + let newParentDepth = 0; + if (newParentId) { + const parent = await repo.get(createNotebookId(newParentId)); + if (parent) newParentDepth = parent.depth; } - } - const result = moveNotebook( - notebook, - newParentId ? createNotebookId(newParentId) : null, - newParentDepth - ); + const result = moveNotebook( + notebook, + newParentId ? createNotebookId(newParentId) : null, + newParentDepth + ); - if (!result.success) { - throw new Error(result.reason); - } + if (!result.success) { + throw new Error(result.reason); + } - await repo.save(result.notebook); + await repo.save(result.notebook); - // Recursively update children's depth to match the new hierarchy - const updateChildrenDepth = async (parentId: string, parentDepth: number) => { - const children = await repo.getChildren(parentId as ReturnType); - for (const child of children) { - const newChildDepth = parentDepth + 1; - if (child.depth !== newChildDepth) { - await repo.save({ ...child, depth: newChildDepth }); - await updateChildrenDepth(child.id, newChildDepth); + const updateChildrenDepth = async (parentId: string, parentDepth: number) => { + const children = await repo.getChildren(parentId as ReturnType); + for (const child of children) { + const newChildDepth = parentDepth + 1; + if (child.depth !== newChildDepth) { + await repo.save({ ...child, depth: newChildDepth }); + await updateChildrenDepth(child.id, newChildDepth); + } } - } - }; - await updateChildrenDepth(result.notebook.id, result.notebook.depth); + }; + await updateChildrenDepth(result.notebook.id, result.notebook.depth); - return { - id: result.notebook.id, - name: result.notebook.name, - parentId: result.notebook.parentId, - depth: result.notebook.depth, - order: result.notebook.order, - createdAt: result.notebook.createdAt, - updatedAt: result.notebook.updatedAt, - }; + return serialize(result.notebook); + }, }); - // Delete notebook - ipcMain.handle('notebooks:delete', async (_event, id: string) => { - const notebookId = createNotebookId(id); - - if (notebookId === INBOX_NOTEBOOK_ID) { - throw new Error('Cannot delete Inbox notebook'); - } - - await repo.delete(notebookId); - return { success: true }; + defineIpcHandler({ + channel: 'notebooks:delete', + args: z.tuple([IdSchema]), + handler: async id => { + const notebookId = createNotebookId(id); + if (notebookId === INBOX_NOTEBOOK_ID) { + throw new Error('Cannot delete Inbox notebook'); + } + await repo.delete(notebookId); + return { success: true }; + }, }); - // Reorder notebooks within a parent - ipcMain.handle( - 'notebooks:reorder', - async (_event, parentId: string | null, orderedIds: string[]) => { + defineIpcHandler({ + channel: 'notebooks:reorder', + args: z.tuple([IdSchema.nullable(), z.array(IdSchema).max(10000)]), + handler: async (parentId, orderedIds) => { await repo.reorder( parentId ? createNotebookId(parentId) : null, orderedIds.map(id => createNotebookId(id)) ); return { success: true }; - } - ); + }, + }); // ═══════════════════════════════════════════════════════════════════════════ // Git Settings per Notebook // ═══════════════════════════════════════════════════════════════════════════ - // Enable git for a notebook - ipcMain.handle('notebooks:enableGit', async (_event, notebookId: string) => { - try { - repo.enableGit(createNotebookId(notebookId)); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to enable git', - }; - } + defineIpcHandler({ + channel: 'notebooks:enableGit', + args: z.tuple([IdSchema]), + handler: notebookId => { + try { + repo.enableGit(createNotebookId(notebookId)); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to enable git', + }; + } + }, }); - // Disable git for a notebook - ipcMain.handle('notebooks:disableGit', async (_event, notebookId: string) => { - try { - repo.disableGit(createNotebookId(notebookId)); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to disable git', - }; - } + defineIpcHandler({ + channel: 'notebooks:disableGit', + args: z.tuple([IdSchema]), + handler: notebookId => { + try { + repo.disableGit(createNotebookId(notebookId)); + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to disable git', + }; + } + }, }); - // Check if git is enabled for a notebook - ipcMain.handle('notebooks:isGitEnabled', async (_event, notebookId: string) => { - try { - const enabled = repo.isGitEnabled(createNotebookId(notebookId)); - return { success: true, enabled }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to check git status', - }; - } + defineIpcHandler({ + channel: 'notebooks:isGitEnabled', + args: z.tuple([IdSchema]), + handler: notebookId => { + try { + const enabled = repo.isGitEnabled(createNotebookId(notebookId)); + return { success: true, enabled }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to check git status', + }; + } + }, }); - // Get git settings for a notebook - ipcMain.handle('notebooks:getGitSettings', async (_event, notebookId: string) => { - try { - const settings = repo.getGitSettings(createNotebookId(notebookId)); - return { success: true, settings }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to get git settings', - }; - } + defineIpcHandler({ + channel: 'notebooks:getGitSettings', + args: z.tuple([IdSchema]), + handler: notebookId => { + try { + const settings = repo.getGitSettings(createNotebookId(notebookId)); + return { success: true, settings }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get git settings', + }; + } + }, }); - // Toggle auto-commit for a notebook - ipcMain.handle( - 'notebooks:setGitAutoCommit', - async (_event, notebookId: string, enabled: boolean) => { + defineIpcHandler({ + channel: 'notebooks:setGitAutoCommit', + args: z.tuple([IdSchema, z.boolean()]), + handler: (notebookId, enabled) => { try { repo.setGitAutoCommit(createNotebookId(notebookId), enabled); return { success: true }; @@ -282,30 +286,22 @@ export function registerNotebookHandlers(deps: NotebookHandlerDeps): void { error: error instanceof Error ? error.message : 'Failed to set auto-commit', }; } - } - ); + }, + }); - // Get all git-enabled notebooks - ipcMain.handle('notebooks:getGitEnabled', async () => { - try { - const notebooks = repo.getGitEnabledNotebooks(); - return { - success: true, - notebooks: notebooks.map(nb => ({ - id: nb.id, - name: nb.name, - parentId: nb.parentId, - depth: nb.depth, - order: nb.order, - createdAt: nb.createdAt, - updatedAt: nb.updatedAt, - })), - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to get git-enabled notebooks', - }; - } + defineIpcHandler({ + channel: 'notebooks:getGitEnabled', + args: z.tuple([]), + handler: () => { + try { + const notebooks = repo.getGitEnabledNotebooks(); + return { success: true, notebooks: notebooks.map(serialize) }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to get git-enabled notebooks', + }; + } + }, }); } diff --git a/apps/desktop/src/main/handlers/pluginHandlers.ts b/apps/desktop/src/main/handlers/pluginHandlers.ts index 5481b8b2..7f52e52b 100644 --- a/apps/desktop/src/main/handlers/pluginHandlers.ts +++ b/apps/desktop/src/main/handlers/pluginHandlers.ts @@ -8,16 +8,27 @@ import { join, normalize, basename } from 'path'; import { readFile, mkdir, rm, readdir, stat, rename } from 'fs/promises'; import { existsSync } from 'fs'; import { execFile } from 'child_process'; +import { writeFile } from 'fs/promises'; import { ipcMain, dialog, BrowserWindow, net } from 'electron'; -import type { DataPaths, Database } from './types.js'; +import { z } from 'zod'; +import { defineIpcHandler } from '../ipc/registry.js'; import { scanPlugins } from '../pluginScanner.js'; -import { writeFile } from 'fs/promises'; +import type { DataPaths, Database } from './types.js'; export interface PluginHandlerDeps { dataPaths: DataPaths; db: Database; } +// Plugin IDs are constrained the same way we enforce on install (regex check +// on manifest.id). Mirror that here so the IPC boundary catches the same shape. +const PluginIdSchema = z + .string() + .min(1) + .max(128) + .regex(/^[a-zA-Z0-9_-]+$/); +const ConfigKeySchema = z.string().min(1).max(256); + export function registerPluginHandlers(deps: PluginHandlerDeps): void { const { dataPaths: paths, db: database } = deps; @@ -25,372 +36,410 @@ export function registerPluginHandlers(deps: PluginHandlerDeps): void { // Plugin Config Persistence // ═══════════════════════════════════════════════════════════════════════════ - ipcMain.handle('pluginConfig:get', (_event, pluginId: string, key: string) => { - const row = database - .prepare('SELECT value FROM plugin_config WHERE plugin_id = ? AND key = ?') - .get(pluginId, key) as { value: string } | undefined; - return row ? JSON.parse(row.value) : undefined; + defineIpcHandler({ + channel: 'pluginConfig:get', + args: z.tuple([PluginIdSchema, ConfigKeySchema]), + handler: (pluginId, key) => { + const row = database + .prepare('SELECT value FROM plugin_config WHERE plugin_id = ? AND key = ?') + .get(pluginId, key) as { value: string } | undefined; + return row ? JSON.parse(row.value) : undefined; + }, }); - ipcMain.handle('pluginConfig:set', (_event, pluginId: string, key: string, value: unknown) => { - database - .prepare('INSERT OR REPLACE INTO plugin_config (plugin_id, key, value) VALUES (?, ?, ?)') - .run(pluginId, key, JSON.stringify(value)); + defineIpcHandler({ + channel: 'pluginConfig:set', + args: z.tuple([PluginIdSchema, ConfigKeySchema, z.unknown()]), + handler: (pluginId, key, value) => { + database + .prepare('INSERT OR REPLACE INTO plugin_config (plugin_id, key, value) VALUES (?, ?, ?)') + .run(pluginId, key, JSON.stringify(value)); + }, }); - ipcMain.handle('pluginConfig:getAll', (_event, pluginId: string) => { - const rows = database - .prepare('SELECT key, value FROM plugin_config WHERE plugin_id = ?') - .all(pluginId) as Array<{ key: string; value: string }>; - const result: Record = {}; - for (const row of rows) { - result[row.key] = JSON.parse(row.value); - } - return result; + defineIpcHandler({ + channel: 'pluginConfig:getAll', + args: z.tuple([PluginIdSchema]), + handler: pluginId => { + const rows = database + .prepare('SELECT key, value FROM plugin_config WHERE plugin_id = ?') + .all(pluginId) as Array<{ key: string; value: string }>; + const result: Record = {}; + for (const row of rows) { + result[row.key] = JSON.parse(row.value); + } + return result; + }, }); - ipcMain.handle('pluginConfig:clear', (_event, pluginId: string) => { - database.prepare('DELETE FROM plugin_config WHERE plugin_id = ?').run(pluginId); + defineIpcHandler({ + channel: 'pluginConfig:clear', + args: z.tuple([PluginIdSchema]), + handler: pluginId => { + database.prepare('DELETE FROM plugin_config WHERE plugin_id = ?').run(pluginId); + }, }); // ═══════════════════════════════════════════════════════════════════════════ // Plugin Discovery // ═══════════════════════════════════════════════════════════════════════════ - // Scan filesystem for plugins - ipcMain.handle('plugins:scan', async () => { - return scanPlugins(paths.plugins); + defineIpcHandler({ + channel: 'plugins:scan', + args: z.tuple([]), + handler: () => scanPlugins(paths.plugins), }); - // Check if a plugin is enabled (default: true if no row exists) - ipcMain.handle('plugins:isEnabled', (_event, pluginId: string) => { - const row = database - .prepare('SELECT enabled FROM plugin_registry WHERE plugin_id = ?') - .get(pluginId) as { enabled: number } | undefined; - return row ? row.enabled === 1 : true; + defineIpcHandler({ + channel: 'plugins:isEnabled', + args: z.tuple([PluginIdSchema]), + handler: pluginId => { + const row = database + .prepare('SELECT enabled FROM plugin_registry WHERE plugin_id = ?') + .get(pluginId) as { enabled: number } | undefined; + return row ? row.enabled === 1 : true; + }, }); - // Set plugin enabled/disabled state - ipcMain.handle('plugins:setEnabled', (_event, pluginId: string, enabled: boolean) => { - database - .prepare( - 'INSERT INTO plugin_registry (plugin_id, enabled) VALUES (?, ?) ON CONFLICT(plugin_id) DO UPDATE SET enabled = ?' - ) - .run(pluginId, enabled ? 1 : 0, enabled ? 1 : 0); + defineIpcHandler({ + channel: 'plugins:setEnabled', + args: z.tuple([PluginIdSchema, z.boolean()]), + handler: (pluginId, enabled) => { + database + .prepare( + 'INSERT INTO plugin_registry (plugin_id, enabled) VALUES (?, ?) ON CONFLICT(plugin_id) DO UPDATE SET enabled = ?' + ) + .run(pluginId, enabled ? 1 : 0, enabled ? 1 : 0); + }, }); - // List all plugin registry state - ipcMain.handle('plugins:listState', () => { - const rows = database.prepare('SELECT plugin_id, enabled FROM plugin_registry').all() as Array<{ - plugin_id: string; - enabled: number; - }>; - return rows.map(row => ({ - pluginId: row.plugin_id, - enabled: row.enabled === 1, - })); + defineIpcHandler({ + channel: 'plugins:listState', + args: z.tuple([]), + handler: () => { + const rows = database + .prepare('SELECT plugin_id, enabled FROM plugin_registry') + .all() as Array<{ + plugin_id: string; + enabled: number; + }>; + return rows.map(row => ({ + pluginId: row.plugin_id, + enabled: row.enabled === 1, + })); + }, }); - // Read init.js user script (returns null if not found) - ipcMain.handle('plugins:readInitScript', async () => { - const initPath = join(paths.root, 'init.js'); - try { - const code = await readFile(initPath, 'utf-8'); - return code; - } catch { - return null; - } + defineIpcHandler({ + channel: 'plugins:readInitScript', + args: z.tuple([]), + handler: async () => { + const initPath = join(paths.root, 'init.js'); + try { + return await readFile(initPath, 'utf-8'); + } catch { + return null; + } + }, }); - // Install plugin from archive (.tar.gz or .zip) - ipcMain.handle('plugins:install', async () => { - const { filePaths, canceled } = await dialog.showOpenDialog({ - title: 'Install Plugin', - properties: ['openFile'], - filters: [{ name: 'Plugin Archive', extensions: ['tar.gz', 'tgz', 'zip'] }], - buttonLabel: 'Install', - }); - - if (canceled || !filePaths[0]) { - return { success: false, error: 'Cancelled' }; - } - - const archivePath = filePaths[0]; - const fileName = basename(archivePath).toLowerCase(); + defineIpcHandler({ + channel: 'plugins:install', + args: z.tuple([]), + handler: async () => { + const { filePaths, canceled } = await dialog.showOpenDialog({ + title: 'Install Plugin', + properties: ['openFile'], + filters: [{ name: 'Plugin Archive', extensions: ['tar.gz', 'tgz', 'zip'] }], + buttonLabel: 'Install', + }); - // Hoist tmpDir so it can be cleaned up in finally - let tmpDir: string | null = null; + if (canceled || !filePaths[0]) { + return { success: false, error: 'Cancelled' }; + } - try { - // Ensure plugins dir exists - await mkdir(paths.plugins, { recursive: true }); + const archivePath = filePaths[0]; + const fileName = basename(archivePath).toLowerCase(); - // Extract to a temp dir first, then move validated plugin folder - tmpDir = join(paths.plugins, `__installing_${Date.now()}`); - const extractDir = tmpDir; - await mkdir(extractDir, { recursive: true }); + // Hoist tmpDir so it can be cleaned up in finally + let tmpDir: string | null = null; - await new Promise((resolve, reject) => { - const cb = (error: Error | null) => { - if (error) reject(error); - else resolve(); - }; - if (fileName.endsWith('.zip')) { - if (process.platform === 'win32') { - execFile( - 'powershell', - [ - '-NoProfile', - '-NonInteractive', - '-Command', - 'Expand-Archive', - '-Force', - '-Path', - archivePath, - '-DestinationPath', - extractDir, - ], - cb - ); + try { + // Ensure plugins dir exists + await mkdir(paths.plugins, { recursive: true }); + + // Extract to a temp dir first, then move validated plugin folder + tmpDir = join(paths.plugins, `__installing_${Date.now()}`); + const extractDir = tmpDir; + await mkdir(extractDir, { recursive: true }); + + await new Promise((resolve, reject) => { + const cb = (error: Error | null) => { + if (error) reject(error); + else resolve(); + }; + if (fileName.endsWith('.zip')) { + if (process.platform === 'win32') { + execFile( + 'powershell', + [ + '-NoProfile', + '-NonInteractive', + '-Command', + 'Expand-Archive', + '-Force', + '-Path', + archivePath, + '-DestinationPath', + extractDir, + ], + cb + ); + } else { + execFile('unzip', ['-o', archivePath, '-d', extractDir], cb); + } } else { - execFile('unzip', ['-o', archivePath, '-d', extractDir], cb); + execFile('tar', ['-xzf', archivePath, '-C', extractDir], cb); + } + }); + + // Find the manifest.json — could be at root or one level deep + const entries = await readdir(extractDir); + let pluginSourceDir = extractDir; + + // If there's a single subdirectory, use that as the plugin root + if (entries.length === 1 && entries[0]) { + const candidatePath = join(extractDir, entries[0]); + const candidateStat = await stat(candidatePath); + if (candidateStat.isDirectory()) { + pluginSourceDir = candidatePath; } - } else { - execFile('tar', ['-xzf', archivePath, '-C', extractDir], cb); } - }); - // Find the manifest.json — could be at root or one level deep - const entries = await readdir(extractDir); - let pluginSourceDir = extractDir; - - // If there's a single subdirectory, use that as the plugin root - if (entries.length === 1 && entries[0]) { - const candidatePath = join(extractDir, entries[0]); - const candidateStat = await stat(candidatePath); - if (candidateStat.isDirectory()) { - pluginSourceDir = candidatePath; + // Validate: must have manifest.json + const manifestPath = join(pluginSourceDir, 'manifest.json'); + if (!existsSync(manifestPath)) { + return { success: false, error: 'No manifest.json found in archive' }; } - } - - // Validate: must have manifest.json - const manifestPath = join(pluginSourceDir, 'manifest.json'); - if (!existsSync(manifestPath)) { - return { success: false, error: 'No manifest.json found in archive' }; - } - const manifestRaw = await readFile(manifestPath, 'utf-8'); - const manifest = JSON.parse(manifestRaw); - if (!manifest.id || !manifest.name) { - return { success: false, error: 'Invalid manifest: missing id or name' }; - } + const manifestRaw = await readFile(manifestPath, 'utf-8'); + const manifest = JSON.parse(manifestRaw); + if (!manifest.id || !manifest.name) { + return { success: false, error: 'Invalid manifest: missing id or name' }; + } - // Validate plugin ID - only allow alphanumeric, hyphens, underscores - if (!/^[a-zA-Z0-9_-]+$/.test(manifest.id)) { - return { - success: false, - error: 'Invalid plugin ID: must be alphanumeric with hyphens/underscores only', - }; - } + // Validate plugin ID - only allow alphanumeric, hyphens, underscores + if (!/^[a-zA-Z0-9_-]+$/.test(manifest.id)) { + return { + success: false, + error: 'Invalid plugin ID: must be alphanumeric with hyphens/underscores only', + }; + } - // Verify path doesn't escape plugins directory - const destDir = join(paths.plugins, manifest.id); - if (!normalize(destDir).startsWith(normalize(paths.plugins))) { - return { success: false, error: 'Invalid plugin ID: path traversal detected' }; - } + // Verify path doesn't escape plugins directory + const destDir = join(paths.plugins, manifest.id); + if (!normalize(destDir).startsWith(normalize(paths.plugins))) { + return { success: false, error: 'Invalid plugin ID: path traversal detected' }; + } - // Move to final destination - if (existsSync(destDir)) { - await rm(destDir, { recursive: true, force: true }); - } + // Move to final destination + if (existsSync(destDir)) { + await rm(destDir, { recursive: true, force: true }); + } - await rename(pluginSourceDir, destDir); + await rename(pluginSourceDir, destDir); - return { success: true, pluginId: manifest.id, pluginName: manifest.name }; - } catch (error) { - return { success: false, error: String(error) }; - } finally { - if (tmpDir && existsSync(tmpDir)) { - await rm(tmpDir, { recursive: true, force: true }).catch(() => {}); + return { success: true, pluginId: manifest.id, pluginName: manifest.name }; + } catch (error) { + return { success: false, error: String(error) }; + } finally { + if (tmpDir && existsSync(tmpDir)) { + await rm(tmpDir, { recursive: true, force: true }).catch(() => {}); + } } - } + }, }); - // Install plugin from a remote URL (marketplace download) - ipcMain.handle('plugins:installFromUrl', async (_event, url: string, pluginSlug: string) => { - // Safety: only allow https URLs - if (!url.startsWith('https://')) { - return { success: false, error: 'Only HTTPS URLs are allowed' }; - } + defineIpcHandler({ + channel: 'plugins:installFromUrl', + args: z.tuple([z.string().url().startsWith('https://').max(2048), PluginIdSchema]), + handler: async (url, pluginSlug) => { + // Safety: only allow https URLs + if (!url.startsWith('https://')) { + return { success: false, error: 'Only HTTPS URLs are allowed' }; + } - // Ensure plugins dir exists - await mkdir(paths.plugins, { recursive: true }); + // Ensure plugins dir exists + await mkdir(paths.plugins, { recursive: true }); - // Download to a temp file inside the plugins dir - const tmpDir = join(paths.plugins, `__downloading_${Date.now()}`); - await mkdir(tmpDir, { recursive: true }); + // Download to a temp file inside the plugins dir + const tmpDir = join(paths.plugins, `__downloading_${Date.now()}`); + await mkdir(tmpDir, { recursive: true }); - try { - const response = await net.fetch(url); - if (!response.ok) { - return { success: false, error: `Download failed: HTTP ${response.status}` }; - } + try { + const response = await net.fetch(url); + if (!response.ok) { + return { success: false, error: `Download failed: HTTP ${response.status}` }; + } - // Limit download size to 50 MB - const MAX_PLUGIN_SIZE = 50 * 1024 * 1024; - const contentLength = response.headers.get('content-length'); - if (contentLength && parseInt(contentLength, 10) > MAX_PLUGIN_SIZE) { - return { success: false, error: 'Plugin archive exceeds maximum size of 50 MB' }; - } + // Limit download size to 50 MB + const MAX_PLUGIN_SIZE = 50 * 1024 * 1024; + const contentLength = response.headers.get('content-length'); + if (contentLength && parseInt(contentLength, 10) > MAX_PLUGIN_SIZE) { + return { success: false, error: 'Plugin archive exceeds maximum size of 50 MB' }; + } - const buffer = Buffer.from(await response.arrayBuffer()); - if (buffer.byteLength > MAX_PLUGIN_SIZE) { - return { success: false, error: 'Plugin archive exceeds maximum size of 50 MB' }; - } + const buffer = Buffer.from(await response.arrayBuffer()); + if (buffer.byteLength > MAX_PLUGIN_SIZE) { + return { success: false, error: 'Plugin archive exceeds maximum size of 50 MB' }; + } - // Determine archive type from URL pathname - const urlPathname = new URL(url).pathname.toLowerCase(); - const isZip = urlPathname.endsWith('.zip'); - const archiveExt = isZip ? '.zip' : '.tar.gz'; - const archivePath = join(tmpDir, `plugin${archiveExt}`); - await writeFile(archivePath, buffer); - - // Extract to a staging dir - const stageDir = join(tmpDir, 'extracted'); - await mkdir(stageDir, { recursive: true }); - - await new Promise((resolve, reject) => { - const cb = (error: Error | null) => { - if (error) reject(error); - else resolve(); - }; - if (isZip) { - if (process.platform === 'win32') { - execFile( - 'powershell', - [ - '-NoProfile', - '-NonInteractive', - '-Command', - 'Expand-Archive', - '-Force', - '-Path', - archivePath, - '-DestinationPath', - stageDir, - ], - cb - ); + // Determine archive type from URL pathname + const urlPathname = new URL(url).pathname.toLowerCase(); + const isZip = urlPathname.endsWith('.zip'); + const archiveExt = isZip ? '.zip' : '.tar.gz'; + const archivePath = join(tmpDir, `plugin${archiveExt}`); + await writeFile(archivePath, buffer); + + // Extract to a staging dir + const stageDir = join(tmpDir, 'extracted'); + await mkdir(stageDir, { recursive: true }); + + await new Promise((resolve, reject) => { + const cb = (error: Error | null) => { + if (error) reject(error); + else resolve(); + }; + if (isZip) { + if (process.platform === 'win32') { + execFile( + 'powershell', + [ + '-NoProfile', + '-NonInteractive', + '-Command', + 'Expand-Archive', + '-Force', + '-Path', + archivePath, + '-DestinationPath', + stageDir, + ], + cb + ); + } else { + execFile('unzip', ['-o', archivePath, '-d', stageDir], cb); + } } else { - execFile('unzip', ['-o', archivePath, '-d', stageDir], cb); + execFile('tar', ['-xzf', archivePath, '-C', stageDir], cb); } - } else { - execFile('tar', ['-xzf', archivePath, '-C', stageDir], cb); - } - }); + }); - // Find manifest.json — could be at root or one level deep - const entries = await readdir(stageDir); - let pluginSourceDir = stageDir; + // Find manifest.json — could be at root or one level deep + const entries = await readdir(stageDir); + let pluginSourceDir = stageDir; - if (entries.length === 1 && entries[0]) { - const candidatePath = join(stageDir, entries[0]); - const candidateStat = await stat(candidatePath); - if (candidateStat.isDirectory()) { - pluginSourceDir = candidatePath; + if (entries.length === 1 && entries[0]) { + const candidatePath = join(stageDir, entries[0]); + const candidateStat = await stat(candidatePath); + if (candidateStat.isDirectory()) { + pluginSourceDir = candidatePath; + } } - } - // Validate: must have manifest.json - const manifestPath = join(pluginSourceDir, 'manifest.json'); - if (!existsSync(manifestPath)) { - return { success: false, error: 'No manifest.json found in downloaded archive' }; - } + // Validate: must have manifest.json + const manifestPath = join(pluginSourceDir, 'manifest.json'); + if (!existsSync(manifestPath)) { + return { success: false, error: 'No manifest.json found in downloaded archive' }; + } - const manifestRaw = await readFile(manifestPath, 'utf-8'); - const manifest = JSON.parse(manifestRaw); - if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) { - return { success: false, error: 'Invalid manifest: not a JSON object' }; - } - if (!manifest.id || !manifest.name) { - return { success: false, error: 'Invalid manifest: missing id or name' }; - } + const manifestRaw = await readFile(manifestPath, 'utf-8'); + const manifest = JSON.parse(manifestRaw); + if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) { + return { success: false, error: 'Invalid manifest: not a JSON object' }; + } + if (!manifest.id || !manifest.name) { + return { success: false, error: 'Invalid manifest: missing id or name' }; + } - // Cross-plugin overwrite protection: if we requested plugin A but the - // archive contains plugin B, block when it would overwrite an existing plugin - if (pluginSlug && manifest.id !== pluginSlug) { - const wouldOverwrite = join(paths.plugins, manifest.id); - if (existsSync(wouldOverwrite)) { + // Cross-plugin overwrite protection: if we requested plugin A but the + // archive contains plugin B, block when it would overwrite an existing plugin + if (pluginSlug && manifest.id !== pluginSlug) { + const wouldOverwrite = join(paths.plugins, manifest.id); + if (existsSync(wouldOverwrite)) { + return { + success: false, + error: `Archive contains "${manifest.id}" but "${pluginSlug}" was requested. Refusing to overwrite existing plugin.`, + }; + } + } + + // Validate plugin ID - only allow alphanumeric, hyphens, underscores + if (!/^[a-zA-Z0-9_-]+$/.test(manifest.id)) { return { success: false, - error: `Archive contains "${manifest.id}" but "${pluginSlug}" was requested. Refusing to overwrite existing plugin.`, + error: 'Invalid plugin ID: must be alphanumeric with hyphens/underscores only', }; } - } - // Validate plugin ID - only allow alphanumeric, hyphens, underscores - if (!/^[a-zA-Z0-9_-]+$/.test(manifest.id)) { + // Verify path doesn't escape plugins directory + const destDir = join(paths.plugins, manifest.id); + if (!normalize(destDir).startsWith(normalize(paths.plugins))) { + return { success: false, error: 'Invalid plugin ID: path traversal detected' }; + } + + // Move to final destination + if (existsSync(destDir)) { + await rm(destDir, { recursive: true, force: true }); + } + + await rename(pluginSourceDir, destDir); + return { - success: false, - error: 'Invalid plugin ID: must be alphanumeric with hyphens/underscores only', + success: true, + pluginId: manifest.id, + pluginName: manifest.name, + slugMismatch: pluginSlug && manifest.id !== pluginSlug ? pluginSlug : undefined, }; + } catch (error) { + return { success: false, error: String(error) }; + } finally { + if (existsSync(tmpDir)) { + await rm(tmpDir, { recursive: true, force: true }).catch(() => {}); + } } + }, + }); - // Verify path doesn't escape plugins directory - const destDir = join(paths.plugins, manifest.id); - if (!normalize(destDir).startsWith(normalize(paths.plugins))) { - return { success: false, error: 'Invalid plugin ID: path traversal detected' }; + defineIpcHandler({ + channel: 'plugins:uninstall', + args: z.tuple([PluginIdSchema]), + handler: async pluginId => { + // Safety: only allow removing from the plugins directory, prevent path traversal + const safeName = pluginId.replace(/[^a-z0-9-]/g, ''); + const pluginDir = join(paths.plugins, safeName); + const normalizedDir = normalize(pluginDir); + + if (!normalizedDir.startsWith(normalize(paths.plugins))) { + return { success: false, error: 'Invalid plugin ID' }; } - // Move to final destination - if (existsSync(destDir)) { - await rm(destDir, { recursive: true, force: true }); + if (!existsSync(pluginDir)) { + return { success: false, error: 'Plugin not found' }; } - await rename(pluginSourceDir, destDir); - - return { - success: true, - pluginId: manifest.id, - pluginName: manifest.name, - slugMismatch: pluginSlug && manifest.id !== pluginSlug ? pluginSlug : undefined, - }; - } catch (error) { - return { success: false, error: String(error) }; - } finally { - // Always clean up temp dir - if (existsSync(tmpDir)) { - await rm(tmpDir, { recursive: true, force: true }).catch(() => {}); + try { + await rm(pluginDir, { recursive: true, force: true }); + database.prepare('DELETE FROM plugin_registry WHERE plugin_id = ?').run(pluginId); + return { success: true }; + } catch (error) { + return { success: false, error: String(error) }; } - } - }); - - // Uninstall plugin (remove its directory) - ipcMain.handle('plugins:uninstall', async (_event, pluginId: string) => { - // Safety: only allow removing from the plugins directory, prevent path traversal - const safeName = pluginId.replace(/[^a-z0-9-]/g, ''); - const pluginDir = join(paths.plugins, safeName); - const normalizedDir = normalize(pluginDir); - - if (!normalizedDir.startsWith(normalize(paths.plugins))) { - return { success: false, error: 'Invalid plugin ID' }; - } - - if (!existsSync(pluginDir)) { - return { success: false, error: 'Plugin not found' }; - } - - try { - await rm(pluginDir, { recursive: true, force: true }); - // Clean up registry entry - database.prepare('DELETE FROM plugin_registry WHERE plugin_id = ?').run(pluginId); - return { success: true }; - } catch (error) { - return { success: false, error: String(error) }; - } + }, }); - // Request plugin reload: broadcast to all windows except sender + // plugins:requestReload uses ipcMain.on (fire-and-forget, not invoke), + // so defineIpcHandler doesn't apply — left raw. ipcMain.on('plugins:requestReload', event => { const senderWebContents = event.sender; for (const win of BrowserWindow.getAllWindows()) { diff --git a/apps/desktop/src/main/handlers/shareHandlers.ts b/apps/desktop/src/main/handlers/shareHandlers.ts index c58f01b6..9f7d03aa 100644 --- a/apps/desktop/src/main/handlers/shareHandlers.ts +++ b/apps/desktop/src/main/handlers/shareHandlers.ts @@ -5,30 +5,44 @@ * Auto-copies the share URL to clipboard. */ -import { ipcMain, clipboard } from 'electron'; +import { clipboard } from 'electron'; +import { z } from 'zod'; +import { defineIpcHandler } from '../ipc/registry.js'; import type { ApiClient } from '../services/apiClient.js'; export interface ShareHandlerDependencies { apiClient: ApiClient; } +// Note content can be quite long; cap at 1 MiB which is well above any +// realistic note and well below "this looks like an attack payload". +const SharePayloadSchema = z.object({ + noteId: z.string().min(1).max(128), + title: z.string().max(512), + content: z.string().max(1024 * 1024), + tags: z.array(z.string().max(64)).max(64).optional(), + backlinks: z + .array(z.object({ noteId: z.string().min(1).max(128), title: z.string().max(512) })) + .max(256) + .optional(), + wordCount: z.number().int().nonnegative().optional(), + notebookName: z.string().max(256).optional(), +}); + +const SlugSchema = z + .string() + .min(1) + .max(128) + .regex(/^[a-zA-Z0-9_-]+$/); + export function registerShareHandlers(deps: ShareHandlerDependencies): void { const { apiClient } = deps; - // Create or update a shared note - ipcMain.handle( - 'share:create', - async ( - _event, - input: { - noteId: string; - title: string; - content: string; - tags?: string[]; - backlinks?: Array<{ noteId: string; title: string }>; - wordCount?: number; - notebookName?: string; - } + defineIpcHandler({ + channel: 'share:create', + args: z.tuple([SharePayloadSchema]), + handler: async ( + input ): Promise<{ success: boolean; url?: string; slug?: string; error?: string }> => { try { const result = await apiClient.shareNote(input); @@ -40,13 +54,13 @@ export function registerShareHandlers(deps: ShareHandlerDependencies): void { error: error instanceof Error ? error.message : 'Failed to share note', }; } - } - ); + }, + }); - // Delete a shared note - ipcMain.handle( - 'share:delete', - async (_event, slug: string): Promise<{ success: boolean; error?: string }> => { + defineIpcHandler({ + channel: 'share:delete', + args: z.tuple([SlugSchema]), + handler: async (slug): Promise<{ success: boolean; error?: string }> => { try { await apiClient.unshareNote(slug); return { success: true }; @@ -56,6 +70,6 @@ export function registerShareHandlers(deps: ShareHandlerDependencies): void { error: error instanceof Error ? error.message : 'Failed to unshare note', }; } - } - ); + }, + }); } diff --git a/apps/desktop/src/main/handlers/updateHandlers.ts b/apps/desktop/src/main/handlers/updateHandlers.ts index 60981db8..6769f8ab 100644 --- a/apps/desktop/src/main/handlers/updateHandlers.ts +++ b/apps/desktop/src/main/handlers/updateHandlers.ts @@ -6,6 +6,8 @@ import { BrowserWindow, ipcMain } from 'electron'; import { autoUpdater } from 'electron-updater'; +import { z } from 'zod'; +import { defineIpcHandler } from '../ipc/registry.js'; import { loggers } from '../logger'; import type { BroadcastFn } from './types.js'; @@ -14,11 +16,10 @@ export interface UpdateHandlerDeps { } export function registerUpdateHandlers(_deps: UpdateHandlerDeps): void { - // Manual check for updates - ipcMain.handle( - 'updates:checkNow', - async (): Promise<{ available: boolean; version?: string }> => { - // In development or without proper updater config, return mock response + defineIpcHandler({ + channel: 'updates:checkNow', + args: z.tuple([]), + handler: async (): Promise<{ available: boolean; version?: string }> => { if (process.env.NODE_ENV === 'development') { return { available: false }; } @@ -29,17 +30,14 @@ export function registerUpdateHandlers(_deps: UpdateHandlerDeps): void { cleanup(); resolve({ available: true, version: info.version }); }; - const onNotAvailable = () => { cleanup(); resolve({ available: false }); }; - const onError = () => { cleanup(); resolve({ available: false }); }; - const cleanup = () => { autoUpdater.removeListener('update-available', onAvailable); autoUpdater.removeListener('update-not-available', onNotAvailable); @@ -55,23 +53,30 @@ export function registerUpdateHandlers(_deps: UpdateHandlerDeps): void { resolve({ available: false }); }); }); - } - ); - - ipcMain.handle('updates:startDownload', async () => { - if (process.env.NODE_ENV === 'development') return { ok: false }; - try { - await autoUpdater.downloadUpdate(); - return { ok: true }; - } catch (err) { - const message = (err as Error).message; - loggers.updater().error({ error: message }, 'Failed to download update'); - return { ok: false, error: message }; - } + }, + }); + + defineIpcHandler({ + channel: 'updates:startDownload', + args: z.tuple([]), + handler: async () => { + if (process.env.NODE_ENV === 'development') return { ok: false }; + try { + await autoUpdater.downloadUpdate(); + return { ok: true }; + } catch (err) { + const message = (err as Error).message; + loggers.updater().error({ error: message }, 'Failed to download update'); + return { ok: false, error: message }; + } + }, }); + // installNow doesn't return a value AND triggers a quit — keeping the + // raw ipcMain.handle is simpler here since registry.ts always wraps in + // Promise and we don't want async semantics interfering with + // the synchronous window-destruction path. ipcMain.handle('updates:installNow', () => { - // Force-close all windows so macOS doesn't block the quit BrowserWindow.getAllWindows().forEach(win => { if (!win.isDestroyed()) win.destroy(); }); @@ -83,7 +88,6 @@ export function registerUpdateHandlers(_deps: UpdateHandlerDeps): void { export function initAutoUpdater(deps: UpdateHandlerDeps): void { const updateLog = loggers.updater(); - // Only check for updates in production if (process.env.NODE_ENV === 'development') { updateLog.debug('Skipping auto-updater in development'); return; @@ -125,7 +129,6 @@ export function initAutoUpdater(deps: UpdateHandlerDeps): void { deps.broadcastToWindows('updates:error', { message: err.message }); }); - // Check for updates after a short delay setTimeout(() => { void autoUpdater.checkForUpdates(); }, 3000); diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index 4c9baceb..4f5d88e5 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -11,8 +11,7 @@ import { initSentry } from './sentry'; initSentry(); import { join, normalize } from 'path'; -import { readFile, writeFile, unlink } from 'fs/promises'; -import { readFileSync, writeFileSync, existsSync } from 'fs'; +import { existsSync } from 'fs'; import { app, BrowserWindow, @@ -31,16 +30,11 @@ import { SQLiteNotebookRepository, } from '@readied/storage-sqlite'; import { createNoteId, createNoteOperation, type NoteStatus } from '@readied/core'; -// eslint-disable-next-line @typescript-eslint/no-deprecated -import type { - LicenseStorage, - StoredTrialData, - StoredLicenseData, - StoredSubscriptionData, -} from '@readied/licensing'; import { initLogger, getLogger, loggers } from './logger'; import { TokenStorage } from './services/tokenStorage.js'; import { AiKeyStorage } from './services/aiKeyStorage.js'; +import { FileLicenseStorage } from './services/fileLicenseStorage.js'; +import { loadWindowState, saveWindowState } from './services/windowState.js'; import { getOrCreateDeviceInfo, type DeviceInfo } from './services/deviceInfo.js'; import { ApiClient } from './services/apiClient.js'; import { EncryptionService } from './services/encryptionService.js'; @@ -139,119 +133,10 @@ export function noteToSnapshot(note: { }; } -// ============================================================================ -// File-based License Storage -// ============================================================================ - -class FileLicenseStorage implements LicenseStorage { - private licensePath: string; - private trialPath: string; - private subscriptionPath: string; - - constructor(dataDir: string) { - this.licensePath = join(dataDir, 'license.json'); - this.trialPath = join(dataDir, 'trial.json'); - this.subscriptionPath = join(dataDir, 'subscription.json'); - } - - async readLicenseData(): Promise { - try { - if (!existsSync(this.licensePath)) { - return null; - } - const content = await readFile(this.licensePath, 'utf-8'); - return JSON.parse(content) as StoredLicenseData; - } catch { - return null; - } - } - - async writeLicenseData(data: StoredLicenseData): Promise { - await writeFile(this.licensePath, JSON.stringify(data, null, 2), 'utf-8'); - } - - async removeLicenseData(): Promise { - if (existsSync(this.licensePath)) { - await unlink(this.licensePath); - } - } - - async readTrialData(): Promise { - try { - if (!existsSync(this.trialPath)) { - return null; - } - const content = await readFile(this.trialPath, 'utf-8'); - return JSON.parse(content) as StoredTrialData; - } catch { - return null; - } - } - - async writeTrialData(data: StoredTrialData): Promise { - await writeFile(this.trialPath, JSON.stringify(data, null, 2), 'utf-8'); - } - - async readSubscriptionData(): Promise { - try { - if (!existsSync(this.subscriptionPath)) { - return null; - } - const content = await readFile(this.subscriptionPath, 'utf-8'); - return JSON.parse(content) as StoredSubscriptionData; - } catch { - return null; - } - } - - async writeSubscriptionData(data: StoredSubscriptionData): Promise { - await writeFile(this.subscriptionPath, JSON.stringify(data, null, 2), 'utf-8'); - } - - async removeSubscriptionData(): Promise { - if (existsSync(this.subscriptionPath)) { - await unlink(this.subscriptionPath); - } - } -} - -// ============================================================================ -// Window State Persistence -// ============================================================================ - -interface WindowState { - x?: number; - y?: number; - width: number; - height: number; - isMaximized?: boolean; -} - -const DEFAULT_WINDOW_STATE: WindowState = { - width: 1200, - height: 800, -}; - -function getWindowStatePath(): string { - return join(app.getPath('userData'), 'window-state.json'); -} - -function loadWindowState(): WindowState { - try { - const data = readFileSync(getWindowStatePath(), 'utf-8'); - return { ...DEFAULT_WINDOW_STATE, ...JSON.parse(data) }; - } catch { - return DEFAULT_WINDOW_STATE; - } -} - -function saveWindowState(state: WindowState): void { - try { - writeFileSync(getWindowStatePath(), JSON.stringify(state, null, 2)); - } catch (err) { - console.error('Failed to save window state:', err); - } -} +// File-based license storage and window state persistence live in +// dedicated modules under ./services/. See: +// - services/fileLicenseStorage.ts +// - services/windowState.ts // ============================================================================ // Initialization diff --git a/apps/desktop/src/main/ipc/registry.ts b/apps/desktop/src/main/ipc/registry.ts new file mode 100644 index 00000000..20484995 --- /dev/null +++ b/apps/desktop/src/main/ipc/registry.ts @@ -0,0 +1,59 @@ +/** + * Typed IPC handler registry. + * + * Wraps `ipcMain.handle()` with Zod validation at the boundary. Renderer + * input is treated as untrusted: if the schema doesn't accept the args, + * the handler throws BEFORE the business logic runs, and the renderer + * sees a structured "invalid args" error instead of a downstream crash. + * + * Pattern: + * + * defineIpcHandler({ + * channel: 'ai:saveKey', + * args: z.tuple([z.string().min(1), z.string().min(1)]), + * handler: (provider, apiKey) => aiKeyStorage.saveKey(provider, apiKey), + * }); + * + * Notes: + * - `args` is a Zod tuple matching the positional renderer arguments. + * Use `z.tuple([])` for no-arg handlers. + * - The schema runs on every invocation. Keep it tight (length caps, + * enums) — schemas are the contract. + */ + +import { ipcMain } from 'electron'; +import { z } from 'zod'; + +export interface DefineIpcHandlerConfig< + Schema extends z.ZodTuple, + Return, +> { + /** IPC channel name (e.g. 'ai:saveKey'). Must be unique. */ + channel: string; + /** Zod tuple describing the positional args sent by the renderer. */ + args: Schema; + /** Business logic. Receives validated args, never raw input. */ + handler: (...args: z.infer) => Promise | Return; +} + +export class IpcValidationError extends Error { + readonly channel: string; + constructor(channel: string, message: string) { + super(`Invalid IPC args for "${channel}": ${message}`); + this.name = 'IpcValidationError'; + this.channel = channel; + } +} + +export function defineIpcHandler< + Schema extends z.ZodTuple, + Return, +>(config: DefineIpcHandlerConfig): void { + ipcMain.handle(config.channel, async (_event, ...rawArgs: unknown[]) => { + const parsed = config.args.safeParse(rawArgs); + if (!parsed.success) { + throw new IpcValidationError(config.channel, parsed.error.message); + } + return config.handler(...(parsed.data as z.infer)); + }); +} diff --git a/apps/desktop/src/main/services/aiKeyStorage.ts b/apps/desktop/src/main/services/aiKeyStorage.ts index 61663dbe..4d72da03 100644 --- a/apps/desktop/src/main/services/aiKeyStorage.ts +++ b/apps/desktop/src/main/services/aiKeyStorage.ts @@ -2,10 +2,23 @@ * AI Key Storage Service * * Securely stores AI provider API keys using Electron's safeStorage API. - * Keys are encrypted with OS-level security (Keychain on macOS, DPAPI on Windows, libsecret on Linux). + * Keys are encrypted with OS-level security (Keychain on macOS, DPAPI on + * Windows, libsecret on Linux). All provider keys live in a single + * encrypted file as a JSON map: * - * All provider keys are stored in a single encrypted file as a JSON map: - * { "anthropic": "sk-ant-...", "openai": "sk-..." } + * { "anthropic": "sk-ant-...", "openai": "sk-..." } + * + * Error handling philosophy: + * - ENOENT on read → no keys yet, return empty map. Safe. + * - "Encryption not available" on read OR write → throw a typed error; + * the caller decides whether to surface it. We do NOT delete the + * stored file in this case — safeStorage may simply be unavailable + * temporarily (locked keychain on macOS after sleep, libsecret not + * running, etc.). Deleting would cause silent data loss. + * - Decryption / JSON parse failure → throw `AiKeyDecryptionError`. + * The previous implementation auto-cleared the file on any decrypt + * error, which is a footgun: if the user's keychain is temporarily + * inaccessible, their keys would vanish. * * @module AiKeyStorage */ @@ -14,123 +27,123 @@ import { promises as fs } from 'fs'; import { join } from 'path'; import { safeStorage } from 'electron'; -// ============================================================================ -// Types -// ============================================================================ - -/** Map of provider name to API key */ type KeyMap = Record; -// ============================================================================ -// AiKeyStorage Class -// ============================================================================ +export class AiKeyEncryptionUnavailableError extends Error { + constructor() { + super( + 'Encryption is not available on this system. ' + + 'On Linux, ensure libsecret (gnome-keyring / kwallet) is running.' + ); + this.name = 'AiKeyEncryptionUnavailableError'; + } +} + +export class AiKeyDecryptionError extends Error { + readonly cause: unknown; + constructor(cause: unknown) { + super( + 'Failed to decrypt AI keys. The OS keychain may be locked or the ' + + 'encrypted file may be corrupt. The stored file was left in place.' + ); + this.name = 'AiKeyDecryptionError'; + this.cause = cause; + } +} export class AiKeyStorage { private readonly filePath: string; /** - * Creates a new AiKeyStorage instance - * @param dataDir - User data directory path (e.g., app.getPath('userData')) + * @param dataDir - User data directory path (e.g. `app.getPath('userData')`) */ constructor(dataDir: string) { this.filePath = join(dataDir, 'ai-keys.encrypted'); } - /** - * Saves an API key for a provider - * @param provider - Provider identifier (e.g., 'anthropic', 'openai') - * @param apiKey - The API key to store - */ async saveKey(provider: string, apiKey: string): Promise { const keys = await this.readKeys(); keys[provider] = apiKey; await this.writeKeys(keys); } - /** - * Retrieves an API key for a provider - * @param provider - Provider identifier - * @returns API key string or null if not found - */ async getKey(provider: string): Promise { const keys = await this.readKeys(); return keys[provider] ?? null; } - /** - * Removes an API key for a provider - * @param provider - Provider identifier - */ async removeKey(provider: string): Promise { const keys = await this.readKeys(); delete keys[provider]; - // If no keys remain, remove the file entirely + // If no keys remain, remove the file entirely. if (Object.keys(keys).length === 0) { - await this.clearAll(); + await this.unlinkFile(); return; } await this.writeKeys(keys); } - /** - * Checks if a key exists for a provider - * @param provider - Provider identifier - * @returns true if a key is stored for this provider - */ async hasKey(provider: string): Promise { const keys = await this.readKeys(); return provider in keys; } - /** - * Lists all providers that have stored keys - * @returns Array of provider identifiers - */ async listProviders(): Promise { const keys = await this.readKeys(); return Object.keys(keys); } - // ========================================================================== - // Private helpers - // ========================================================================== - /** - * Reads and decrypts the key map from disk - * @returns Parsed key map, or empty object if file doesn't exist + * Read and decrypt the key map. + * + * Returns `{}` if no file exists yet. Throws on every other failure mode + * so the caller can decide how to surface the problem instead of silently + * losing state. */ private async readKeys(): Promise { + let encrypted: Buffer; try { - const encrypted = await fs.readFile(this.filePath); - const plaintext = safeStorage.decryptString(encrypted); - const keys = JSON.parse(plaintext) as KeyMap; - - // Validate structure: must be a plain object with string values - if (typeof keys !== 'object' || keys === null || Array.isArray(keys)) { - throw new Error('Invalid key map structure'); - } - - return keys; + encrypted = await fs.readFile(this.filePath); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { - // File doesn't exist - no keys saved yet return {}; } - // Decryption or parsing failed - clear corrupted file - await this.clearAll(); - return {}; + throw error; + } + + if (!safeStorage.isEncryptionAvailable()) { + throw new AiKeyEncryptionUnavailableError(); + } + + let plaintext: string; + try { + plaintext = safeStorage.decryptString(encrypted); + } catch (cause) { + throw new AiKeyDecryptionError(cause); + } + + let keys: unknown; + try { + keys = JSON.parse(plaintext); + } catch (cause) { + throw new AiKeyDecryptionError(cause); } + + if (typeof keys !== 'object' || keys === null || Array.isArray(keys)) { + throw new AiKeyDecryptionError(new Error('Decrypted payload is not a JSON object')); + } + + // We trust the shape because we wrote it. The handler boundary + // (defineIpcHandler in aiKeyHandlers.ts) already validates keys + // before they're written, so the saved map only contains strings. + return keys as KeyMap; } - /** - * Encrypts and writes the key map to disk - * @param keys - The key map to persist - */ private async writeKeys(keys: KeyMap): Promise { if (!safeStorage.isEncryptionAvailable()) { - throw new Error('Encryption is not available on this system'); + throw new AiKeyEncryptionUnavailableError(); } const plaintext = JSON.stringify(keys); @@ -138,17 +151,13 @@ export class AiKeyStorage { await fs.writeFile(this.filePath, encrypted); } - /** - * Removes the encrypted file from disk - */ - private async clearAll(): Promise { + private async unlinkFile(): Promise { try { await fs.unlink(this.filePath); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { throw error; } - // File doesn't exist - already clear } } } diff --git a/apps/desktop/src/main/services/fileLicenseStorage.ts b/apps/desktop/src/main/services/fileLicenseStorage.ts new file mode 100644 index 00000000..1c29e70c --- /dev/null +++ b/apps/desktop/src/main/services/fileLicenseStorage.ts @@ -0,0 +1,138 @@ +/** + * File-backed implementation of @readied/licensing's LicenseStorage. + * + * Persists three small JSON files under the user's data directory: + * + * license.json — legacy LicenseFile (StoredLicenseData) + * trial.json — local trial start (StoredTrialData) + * subscription.json — cached subscription state (StoredSubscriptionData) + * + * Subscription verification (Ed25519): + * - If the persisted cache contains `signedEnvelope`, the read path + * verifies it via @readied/licensing's verifySubscriptionSignature + * before returning. An invalid envelope causes the cache to be + * refused (read returns null) so the next call falls through to a + * fresh fetch from the API. + * - If the persisted cache has NO `signedEnvelope`, we accept it and + * log a structured warning. This is the migration window: once the + * server reliably emits envelopes for N releases, we can flip to + * strict mode (refuse unsigned caches). + * - trial.json is unsigned by design (see packages/licensing/README.md). + */ + +import { readFile, writeFile, unlink } from 'fs/promises'; +import { existsSync } from 'fs'; +import { join } from 'path'; +import { + verifySubscriptionSignature, + type LicenseStorage, + type StoredLicenseData, + type StoredTrialData, + type StoredSubscriptionData, +} from '@readied/licensing'; +import { loggers } from '../logger'; + +/** + * Ed25519 public key used to verify SignedSubscriptionEnvelope payloads. + * + * Public-by-design: the client needs it to verify. The matching PRIVATE + * key MUST live ONLY on the licensing server (env var, never the repo). + * + * Rotation procedure when this key needs to change: + * 1. Generate a new keypair on a trusted machine + * (see packages/licensing/README.md > "Rolling the signing key") + * 2. Ship a desktop release with the new public key embedded HERE + * 3. Wait for the install base to update + * 4. Switch the server to sign with the new private key + * Clients on the old release will stop verifying envelopes signed + * with the new key, falling back to the "no-envelope" lenient log — + * no hard lockout, but they'll re-fetch on every cache miss. + */ +const SUBSCRIPTION_PUBLIC_KEY = 'd049019b2ff05ccfd3802e0619d5897e21431a6f946af724c13ed7ecca7ec01f'; + +export class FileLicenseStorage implements LicenseStorage { + private readonly licensePath: string; + private readonly trialPath: string; + private readonly subscriptionPath: string; + + constructor(dataDir: string) { + this.licensePath = join(dataDir, 'license.json'); + this.trialPath = join(dataDir, 'trial.json'); + this.subscriptionPath = join(dataDir, 'subscription.json'); + } + + async readLicenseData(): Promise { + return readJsonOrNull(this.licensePath); + } + + async writeLicenseData(data: StoredLicenseData): Promise { + await writeFile(this.licensePath, JSON.stringify(data, null, 2), 'utf-8'); + } + + async removeLicenseData(): Promise { + if (existsSync(this.licensePath)) { + await unlink(this.licensePath); + } + } + + async readTrialData(): Promise { + return readJsonOrNull(this.trialPath); + } + + async writeTrialData(data: StoredTrialData): Promise { + await writeFile(this.trialPath, JSON.stringify(data, null, 2), 'utf-8'); + } + + async readSubscriptionData(): Promise { + const cached = await readJsonOrNull(this.subscriptionPath); + if (!cached) return null; + + if (!cached.signedEnvelope) { + // Migration window: no envelope on disk. Accept the cache, log so + // operators can see when the population is fully migrated. + loggers + .license() + .warn( + { hasSubscriptionId: Boolean(cached.subscription?.subscriptionId) }, + 'subscription cache has no signed envelope — running in lenient mode' + ); + return cached; + } + + const result = await verifySubscriptionSignature(cached.signedEnvelope, { + publicKey: SUBSCRIPTION_PUBLIC_KEY, + }); + if (!result.valid) { + loggers + .license() + .error( + { error: result.error }, + 'subscription cache envelope failed verification — refusing cache, will refetch' + ); + // Refuse the cache. The next caller will fetch from the API. + return null; + } + + return cached; + } + + async writeSubscriptionData(data: StoredSubscriptionData): Promise { + await writeFile(this.subscriptionPath, JSON.stringify(data, null, 2), 'utf-8'); + } + + async removeSubscriptionData(): Promise { + if (existsSync(this.subscriptionPath)) { + await unlink(this.subscriptionPath); + } + } +} + +async function readJsonOrNull(path: string): Promise { + try { + if (!existsSync(path)) return null; + const content = await readFile(path, 'utf-8'); + return JSON.parse(content) as T; + } catch { + return null; + } +} diff --git a/apps/desktop/src/main/services/windowState.ts b/apps/desktop/src/main/services/windowState.ts new file mode 100644 index 00000000..ee6a90f1 --- /dev/null +++ b/apps/desktop/src/main/services/windowState.ts @@ -0,0 +1,48 @@ +/** + * Window position/size persistence. + * + * Saved to `window-state.json` under Electron's `userData` directory so + * the desktop reopens the last window in the same place across launches. + * + * Sync file I/O is intentional — `loadWindowState` is called during + * window construction before the renderer mounts, and `saveWindowState` + * runs during window close where event handlers don't await. + */ + +import { readFileSync, writeFileSync } from 'fs'; +import { join } from 'path'; +import { app } from 'electron'; + +export interface WindowState { + x?: number; + y?: number; + width: number; + height: number; + isMaximized?: boolean; +} + +export const DEFAULT_WINDOW_STATE: WindowState = { + width: 1200, + height: 800, +}; + +function getWindowStatePath(): string { + return join(app.getPath('userData'), 'window-state.json'); +} + +export function loadWindowState(): WindowState { + try { + const data = readFileSync(getWindowStatePath(), 'utf-8'); + return { ...DEFAULT_WINDOW_STATE, ...JSON.parse(data) }; + } catch { + return DEFAULT_WINDOW_STATE; + } +} + +export function saveWindowState(state: WindowState): void { + try { + writeFileSync(getWindowStatePath(), JSON.stringify(state, null, 2)); + } catch (err) { + console.error('Failed to save window state:', err); + } +} diff --git a/apps/desktop/src/renderer/analytics.ts b/apps/desktop/src/renderer/analytics.ts deleted file mode 100644 index 5e7dc712..00000000 --- a/apps/desktop/src/renderer/analytics.ts +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Analytics Module - Offline-First Event Tracking - * - * Privacy-respecting analytics that works offline. - * Events are queued when offline and synced when online. - * - * Setup: - * 1. Create account at https://app.posthog.com (free tier: 1M events/mo) - * 2. Set VITE_POSTHOG_KEY in your environment - * 3. Or use your own endpoint with VITE_ANALYTICS_ENDPOINT - */ - -interface AnalyticsEvent { - name: string; - properties?: Record; - timestamp: number; -} - -// Configuration -const POSTHOG_KEY = import.meta.env.VITE_POSTHOG_KEY || ''; -const ANALYTICS_ENDPOINT = import.meta.env.VITE_ANALYTICS_ENDPOINT || ''; -const QUEUE_KEY = 'readied_analytics_queue'; -const MAX_QUEUE_SIZE = 100; - -// Event queue for offline support -let eventQueue: AnalyticsEvent[] = []; - -// Load queue from localStorage on init -function loadQueue(): void { - try { - const stored = localStorage.getItem(QUEUE_KEY); - if (stored) { - eventQueue = JSON.parse(stored); - } - } catch { - eventQueue = []; - } -} - -// Save queue to localStorage -function saveQueue(): void { - try { - // Trim queue if too large - if (eventQueue.length > MAX_QUEUE_SIZE) { - eventQueue = eventQueue.slice(-MAX_QUEUE_SIZE); - } - localStorage.setItem(QUEUE_KEY, JSON.stringify(eventQueue)); - } catch { - // Ignore storage errors - } -} - -// Check if analytics is enabled -function isEnabled(): boolean { - // Disabled if no key configured - if (!POSTHOG_KEY && !ANALYTICS_ENDPOINT) { - return false; - } - - // Respect user preference (could add opt-out UI) - const optOut = localStorage.getItem('readied_analytics_optout'); - return optOut !== 'true'; -} - -/** - * Track an event - */ -export function track(name: string, properties?: Record): void { - if (!isEnabled()) return; - - const event: AnalyticsEvent = { - name, - properties: { - ...properties, - app_version: window.readied?.app ? 'readied' : 'unknown', - }, - timestamp: Date.now(), - }; - - eventQueue.push(event); - saveQueue(); - - // Try to flush immediately if online - if (navigator.onLine) { - void flush(); - } -} - -/** - * Flush queued events to server - */ -async function flush(): Promise { - if (eventQueue.length === 0) return; - if (!navigator.onLine) return; - - const events = [...eventQueue]; - eventQueue = []; - saveQueue(); - - try { - if (POSTHOG_KEY) { - // PostHog batch API - await fetch('https://app.posthog.com/batch/', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - api_key: POSTHOG_KEY, - batch: events.map(e => ({ - event: e.name, - properties: e.properties, - timestamp: new Date(e.timestamp).toISOString(), - })), - }), - }); - } else if (ANALYTICS_ENDPOINT) { - // Custom endpoint - await fetch(ANALYTICS_ENDPOINT, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ events }), - }); - } - } catch { - // Re-queue events on failure - eventQueue = [...events, ...eventQueue]; - saveQueue(); - } -} - -/** - * Opt out of analytics - */ -export function optOut(): void { - localStorage.setItem('readied_analytics_optout', 'true'); - eventQueue = []; - saveQueue(); -} - -/** - * Opt back in to analytics - */ -export function optIn(): void { - localStorage.removeItem('readied_analytics_optout'); -} - -/** - * Check if user has opted out - */ -export function hasOptedOut(): boolean { - return localStorage.getItem('readied_analytics_optout') === 'true'; -} - -// Initialize -loadQueue(); - -// Flush on online -window.addEventListener('online', flush); - -// Flush before unload -window.addEventListener('beforeunload', flush); - -// Periodic flush (every 30 seconds if online) -setInterval(() => { - if (navigator.onLine && eventQueue.length > 0) { - void flush(); - } -}, 30000); - -// ===== PREDEFINED EVENTS ===== - -export const Analytics = { - // App lifecycle - appLaunched: () => track('app_launched'), - appClosed: () => track('app_closed'), - - // Notes - noteCreated: () => track('note_created'), - noteDeleted: () => track('note_deleted'), - noteExported: (format: string) => track('note_exported', { format }), - - // Features - featureUsed: (feature: string) => track('feature_used', { feature }), - searchUsed: () => track('search_used'), - graphViewOpened: () => track('graph_view_opened'), - backupCreated: () => track('backup_created'), - - // Errors (also sent to Sentry) - errorOccurred: (error: string) => track('error_occurred', { error }), -}; diff --git a/apps/desktop/src/renderer/components/MarkdownEditor.tsx b/apps/desktop/src/renderer/components/MarkdownEditor.tsx index 2e1a6822..f24c2a6a 100644 --- a/apps/desktop/src/renderer/components/MarkdownEditor.tsx +++ b/apps/desktop/src/renderer/components/MarkdownEditor.tsx @@ -16,13 +16,7 @@ import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirro import { indentUnit } from '@codemirror/language'; import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; import { languages } from '@codemirror/language-data'; -import { - syntaxHighlighting, - HighlightStyle, - indentOnInput, - bracketMatching, -} from '@codemirror/language'; -import { tags } from '@lezer/highlight'; +import { syntaxHighlighting, indentOnInput, bracketMatching } from '@codemirror/language'; import { toggleBold, toggleItalic, @@ -51,6 +45,7 @@ import { htmlToGfmMarkdown } from '../utils/htmlToMarkdown'; import { useEditorBufferStore } from '../stores/editorBufferStore'; import { useSettingsStore, selectEditor } from '../stores/settings'; import { setEditorView } from '../hooks/useCommandRegistry'; +import { createEditorTheme, markdownHighlighting, SCROLL_PAST_END_PADDING } from './editorTheme.js'; // Compartments for dynamic settings const lineNumbersCompartment = new Compartment(); @@ -61,132 +56,8 @@ const tabSizeCompartment = new Compartment(); const scrollPastEndCompartment = new Compartment(); const spellCheckCompartment = new Compartment(); -/** Scroll past end padding - allows scrolling content to top of viewport */ -const SCROLL_PAST_END_PADDING = '50vh'; - -/** Create theme with configurable settings (uses CSS variables for colors) */ -function createEditorTheme(fontSize: number, fontFamily: string, lineHeight: number) { - return EditorView.theme({ - '&': { - backgroundColor: 'transparent', - color: 'var(--cm-text)', - fontSize: `${fontSize}px`, - height: '100%', - }, - '.cm-content': { - fontFamily: fontFamily || "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace", - padding: '12px', - lineHeight: String(lineHeight), - caretColor: 'var(--cm-cursor)', - }, - '.cm-cursor': { - borderLeftColor: 'var(--cm-cursor)', - borderLeftWidth: '2px', - }, - '.cm-selectionBackground, &.cm-focused .cm-selectionBackground': { - backgroundColor: 'var(--cm-selection)', - }, - '.cm-activeLine': { - backgroundColor: 'var(--cm-active-line)', - }, - '.cm-activeLineGutter': { - backgroundColor: 'var(--cm-active-line)', - }, - '.cm-gutters': { - backgroundColor: 'transparent', - borderRight: '1px solid var(--cm-gutter-border)', - color: 'var(--cm-gutter-text)', - }, - '.cm-lineNumbers .cm-gutterElement': { - padding: '0 12px 0 16px', - minWidth: '40px', - }, - '.cm-scroller': { - overflow: 'auto', - }, - '.cm-line': { - padding: '0 4px', - }, - '&.cm-focused .cm-matchingBracket': { - backgroundColor: 'var(--cm-bracket-match)', - outline: 'none', - }, - // Autocomplete tooltip - '.cm-tooltip-autocomplete': { - backgroundColor: 'var(--cm-tooltip-bg)', - backdropFilter: 'blur(12px)', - border: '1px solid var(--cm-tooltip-border)', - borderRadius: '8px', - boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)', - overflow: 'hidden', - }, - '.cm-tooltip-autocomplete > ul': { - fontFamily: "'Inter', -apple-system, sans-serif", - fontSize: '13px', - maxHeight: '300px', - }, - '.cm-tooltip-autocomplete > ul > li': { - padding: '8px 12px', - color: 'var(--cm-tooltip-text)', - cursor: 'pointer', - }, - '.cm-tooltip-autocomplete > ul > li[aria-selected]': { - backgroundColor: 'var(--accent-muted)', - color: 'var(--accent)', - }, - '.cm-completionLabel': { - fontWeight: '500', - }, - }); -} - -/** Syntax highlighting for Markdown (uses CSS variables for theme-aware colors) */ -const markdownHighlighting = HighlightStyle.define([ - // Headings - { tag: tags.heading1, color: 'var(--cm-heading)', fontWeight: '700', fontSize: '1.5em' }, - { tag: tags.heading2, color: 'var(--cm-heading)', fontWeight: '600', fontSize: '1.3em' }, - { tag: tags.heading3, color: 'var(--cm-heading)', fontWeight: '600', fontSize: '1.15em' }, - { tag: tags.heading4, color: 'var(--cm-heading)', fontWeight: '600' }, - { tag: tags.heading5, color: 'var(--cm-heading)', fontWeight: '600' }, - { tag: tags.heading6, color: 'var(--cm-heading)', fontWeight: '600' }, - - // Emphasis - { tag: tags.emphasis, fontStyle: 'italic', color: 'var(--cm-emphasis)' }, - { tag: tags.strong, fontWeight: '700', color: 'var(--cm-strong)' }, - { tag: tags.strikethrough, textDecoration: 'line-through', color: 'var(--cm-strikethrough)' }, - - // Code - { - tag: tags.monospace, - fontFamily: "'JetBrains Mono', monospace", - backgroundColor: 'var(--cm-code-bg)', - padding: '2px 4px', - borderRadius: '3px', - }, - - // Links - { tag: tags.link, color: 'var(--cm-link)', textDecoration: 'underline' }, - { tag: tags.url, color: 'var(--cm-link)' }, - - // Lists - { tag: tags.list, color: 'var(--cm-list)' }, - - // Quotes - { - tag: tags.quote, - color: 'var(--cm-quote)', - fontStyle: 'italic', - borderLeft: '3px solid var(--cm-quote-border)', - paddingLeft: '12px', - }, - - // Meta (like --- for frontmatter) - { tag: tags.meta, color: 'var(--cm-meta)' }, - { tag: tags.comment, color: 'var(--cm-meta)' }, - - // Punctuation - { tag: tags.processingInstruction, color: 'var(--cm-meta)' }, -]); +// createEditorTheme, markdownHighlighting, and SCROLL_PAST_END_PADDING +// live in editorTheme.ts. interface MarkdownEditorProps { initialContent: string; @@ -360,6 +231,18 @@ export const MarkdownEditor = forwardRef { + console.error('[CodeMirror] plugin error:', err); + const sentry = ( + globalThis as unknown as { + Sentry?: { captureException: (e: unknown, ctx?: unknown) => void }; + } + ).Sentry; + sentry?.captureException(err, { tags: { source: 'codemirror' } }); + }), + // Configurable: Line numbers lineNumbersCompartment.of(showLineNumbers ? lineNumbers() : []), diff --git a/apps/desktop/src/renderer/components/auth/MagicLinkFlow.tsx b/apps/desktop/src/renderer/components/auth/MagicLinkFlow.tsx index cd0d0854..9a914db6 100644 --- a/apps/desktop/src/renderer/components/auth/MagicLinkFlow.tsx +++ b/apps/desktop/src/renderer/components/auth/MagicLinkFlow.tsx @@ -6,7 +6,7 @@ import { useState, useCallback, useEffect, useRef, FormEvent } from 'react'; import { Mail, CheckCircle, AlertCircle, X, RefreshCw } from 'lucide-react'; -import { useAuthStore } from '../../stores/authStore'; +import { useAuthStore, selectIsAuthenticated, selectError } from '../../stores/authStore'; import styles from './MagicLinkFlow.module.css'; export interface MagicLinkFlowProps { @@ -17,7 +17,9 @@ export interface MagicLinkFlowProps { type Step = 'email' | 'sent' | 'verifying' | 'success' | 'error'; export function MagicLinkFlow({ onSuccess, onCancel }: MagicLinkFlowProps) { - const { requestMagicLink, isAuthenticated, error: authError } = useAuthStore(); + const requestMagicLink = useAuthStore(state => state.requestMagicLink); + const isAuthenticated = useAuthStore(selectIsAuthenticated); + const authError = useAuthStore(selectError); const [step, setStep] = useState('email'); const [email, setEmail] = useState(''); const [error, setError] = useState(null); diff --git a/apps/desktop/src/renderer/components/editorTheme.ts b/apps/desktop/src/renderer/components/editorTheme.ts new file mode 100644 index 00000000..5b249c1d --- /dev/null +++ b/apps/desktop/src/renderer/components/editorTheme.ts @@ -0,0 +1,139 @@ +/** + * CodeMirror theme + syntax highlighting for Readied's MarkdownEditor. + * + * Pure values extracted from MarkdownEditor.tsx so theme tweaks don't + * force a rebuild of the entire editor file. Colors come from CSS + * variables (defined in renderer/styles/) so light/dark switching works + * without rebuilding the EditorView. + */ + +import { EditorView } from '@codemirror/view'; +import { HighlightStyle } from '@codemirror/language'; +import { tags } from '@lezer/highlight'; + +/** Padding under the document so the user can scroll the last line near the top. */ +export const SCROLL_PAST_END_PADDING = '50vh'; + +/** Build a CodeMirror theme bound to the user's font/size preferences. */ +export function createEditorTheme(fontSize: number, fontFamily: string, lineHeight: number) { + return EditorView.theme({ + '&': { + backgroundColor: 'transparent', + color: 'var(--cm-text)', + fontSize: `${fontSize}px`, + height: '100%', + }, + '.cm-content': { + fontFamily: fontFamily || "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace", + padding: '12px', + lineHeight: String(lineHeight), + caretColor: 'var(--cm-cursor)', + }, + '.cm-cursor': { + borderLeftColor: 'var(--cm-cursor)', + borderLeftWidth: '2px', + }, + '.cm-selectionBackground, &.cm-focused .cm-selectionBackground': { + backgroundColor: 'var(--cm-selection)', + }, + '.cm-activeLine': { + backgroundColor: 'var(--cm-active-line)', + }, + '.cm-activeLineGutter': { + backgroundColor: 'var(--cm-active-line)', + }, + '.cm-gutters': { + backgroundColor: 'transparent', + borderRight: '1px solid var(--cm-gutter-border)', + color: 'var(--cm-gutter-text)', + }, + '.cm-lineNumbers .cm-gutterElement': { + padding: '0 12px 0 16px', + minWidth: '40px', + }, + '.cm-scroller': { + overflow: 'auto', + }, + '.cm-line': { + padding: '0 4px', + }, + '&.cm-focused .cm-matchingBracket': { + backgroundColor: 'var(--cm-bracket-match)', + outline: 'none', + }, + // Autocomplete tooltip + '.cm-tooltip-autocomplete': { + backgroundColor: 'var(--cm-tooltip-bg)', + backdropFilter: 'blur(12px)', + border: '1px solid var(--cm-tooltip-border)', + borderRadius: '8px', + boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)', + overflow: 'hidden', + }, + '.cm-tooltip-autocomplete > ul': { + fontFamily: "'Inter', -apple-system, sans-serif", + fontSize: '13px', + maxHeight: '300px', + }, + '.cm-tooltip-autocomplete > ul > li': { + padding: '8px 12px', + color: 'var(--cm-tooltip-text)', + cursor: 'pointer', + }, + '.cm-tooltip-autocomplete > ul > li[aria-selected]': { + backgroundColor: 'var(--accent-muted)', + color: 'var(--accent)', + }, + '.cm-completionLabel': { + fontWeight: '500', + }, + }); +} + +/** Syntax highlighting for Markdown — uses CSS variables so dark/light works. */ +export const markdownHighlighting = HighlightStyle.define([ + // Headings + { tag: tags.heading1, color: 'var(--cm-heading)', fontWeight: '700', fontSize: '1.5em' }, + { tag: tags.heading2, color: 'var(--cm-heading)', fontWeight: '600', fontSize: '1.3em' }, + { tag: tags.heading3, color: 'var(--cm-heading)', fontWeight: '600', fontSize: '1.15em' }, + { tag: tags.heading4, color: 'var(--cm-heading)', fontWeight: '600' }, + { tag: tags.heading5, color: 'var(--cm-heading)', fontWeight: '600' }, + { tag: tags.heading6, color: 'var(--cm-heading)', fontWeight: '600' }, + + // Emphasis + { tag: tags.emphasis, fontStyle: 'italic', color: 'var(--cm-emphasis)' }, + { tag: tags.strong, fontWeight: '700', color: 'var(--cm-strong)' }, + { tag: tags.strikethrough, textDecoration: 'line-through', color: 'var(--cm-strikethrough)' }, + + // Code + { + tag: tags.monospace, + fontFamily: "'JetBrains Mono', monospace", + backgroundColor: 'var(--cm-code-bg)', + padding: '2px 4px', + borderRadius: '3px', + }, + + // Links + { tag: tags.link, color: 'var(--cm-link)', textDecoration: 'underline' }, + { tag: tags.url, color: 'var(--cm-link)' }, + + // Lists + { tag: tags.list, color: 'var(--cm-list)' }, + + // Quotes + { + tag: tags.quote, + color: 'var(--cm-quote)', + fontStyle: 'italic', + borderLeft: '3px solid var(--cm-quote-border)', + paddingLeft: '12px', + }, + + // Meta (like --- for frontmatter) + { tag: tags.meta, color: 'var(--cm-meta)' }, + { tag: tags.comment, color: 'var(--cm-meta)' }, + + // Punctuation + { tag: tags.processingInstruction, color: 'var(--cm-meta)' }, +]); diff --git a/apps/desktop/src/renderer/components/sync/EnableSyncModal.tsx b/apps/desktop/src/renderer/components/sync/EnableSyncModal.tsx index 431cc1e0..d83e7ab9 100644 --- a/apps/desktop/src/renderer/components/sync/EnableSyncModal.tsx +++ b/apps/desktop/src/renderer/components/sync/EnableSyncModal.tsx @@ -11,9 +11,9 @@ import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { Cloud, Mail, CheckCircle, X, RefreshCw, Sparkles } from 'lucide-react'; -import { useAuthStore } from '../../stores/authStore'; -import { useLicense } from '../../contexts/LicenseContext'; import { getProductConfig } from '@readied/product-config'; +import { useAuthStore, selectIsAuthenticated, selectError } from '../../stores/authStore'; +import { useLicense } from '../../contexts/LicenseContext'; import styles from './LoginModal.module.css'; interface EnableSyncModalProps { @@ -46,7 +46,9 @@ export function EnableSyncModal({ isOpen, onClose }: EnableSyncModalProps) { const [isResending, setIsResending] = useState(false); const timerRef = useRef | null>(null); - const { requestMagicLink, isAuthenticated, error: authError } = useAuthStore(); + const requestMagicLink = useAuthStore(state => state.requestMagicLink); + const isAuthenticated = useAuthStore(selectIsAuthenticated); + const authError = useAuthStore(selectError); const { state: licenseState, openSubscribe } = useLicense(); const config = useMemo(() => getProductConfig(), []); const proPricing = config.plans.pro.pricing!; diff --git a/apps/desktop/src/renderer/hooks/useTheme.ts b/apps/desktop/src/renderer/hooks/useTheme.ts deleted file mode 100644 index 60b4a3e0..00000000 --- a/apps/desktop/src/renderer/hooks/useTheme.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Theme Hook - * - * Applies theme and accent color to document based on settings. - * Supports: 'dark', 'light', 'system' + custom accentColor - */ - -import { useEffect } from 'react'; -import { useSettingsStore, selectAppearance } from '../stores/settings'; - -type Theme = 'dark' | 'light' | 'system'; - -/** - * Get the resolved theme (dark or light) based on preference - */ -function resolveTheme(preference: Theme): 'dark' | 'light' { - if (preference === 'system') { - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; - } - return preference; -} - -/** - * Apply theme to document root - */ -function applyTheme(theme: 'dark' | 'light') { - document.documentElement.setAttribute('data-theme', theme); - - // Also update meta theme-color for native UI - const metaThemeColor = document.querySelector('meta[name="theme-color"]'); - const color = theme === 'dark' ? '#0a0b0d' : '#ffffff'; - if (metaThemeColor) { - metaThemeColor.setAttribute('content', color); - } -} - -/** - * Parse hex color to RGB components - */ -function hexToRgb(hex: string): { r: number; g: number; b: number } | null { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? { - r: parseInt(result[1]!, 16), - g: parseInt(result[2]!, 16), - b: parseInt(result[3]!, 16), - } - : null; -} - -/** - * Darken a hex color by a percentage - */ -function darkenHex(hex: string, percent: number): string { - const rgb = hexToRgb(hex); - if (!rgb) return hex; - const factor = 1 - percent / 100; - const r = Math.round(rgb.r * factor); - const g = Math.round(rgb.g * factor); - const b = Math.round(rgb.b * factor); - return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; -} - -/** - * Apply accent color to CSS custom properties - */ -function applyAccentColor(hex: string, theme: 'dark' | 'light') { - const root = document.documentElement; - const rgb = hexToRgb(hex); - - if (!rgb) return; - - // Main accent color - root.style.setProperty('--accent', hex); - - // Muted version (for backgrounds) - const mutedOpacity = theme === 'dark' ? 0.15 : 0.12; - root.style.setProperty('--accent-muted', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${mutedOpacity})`); - - // Strong version (darker for buttons on hover) - root.style.setProperty('--accent-strong', darkenHex(hex, 15)); - - // Also update CodeMirror accent-related tokens - root.style.setProperty('--cm-heading', hex); - root.style.setProperty('--cm-cursor', hex); - root.style.setProperty('--cm-selection', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2)`); - root.style.setProperty('--cm-bracket-match', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.3)`); - root.style.setProperty('--cm-quote-border', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.5)`); -} - -/** - * Hook to manage theme and accent color based on settings - */ -export function useTheme() { - const appearance = useSettingsStore(selectAppearance); - const { theme: themePreference, accentColor } = appearance; - - // Apply theme - useEffect(() => { - const resolved = resolveTheme(themePreference); - applyTheme(resolved); - - // If system preference, listen for changes - if (themePreference === 'system') { - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - - const handleChange = (e: MediaQueryListEvent) => { - applyTheme(e.matches ? 'dark' : 'light'); - }; - - mediaQuery.addEventListener('change', handleChange); - return () => mediaQuery.removeEventListener('change', handleChange); - } - }, [themePreference]); - - // Apply accent color - useEffect(() => { - const resolved = resolveTheme(themePreference); - applyAccentColor(accentColor, resolved); - }, [accentColor, themePreference]); - - return { - theme: themePreference, - resolvedTheme: resolveTheme(themePreference), - }; -} diff --git a/apps/desktop/src/renderer/pages/settings/sections/AccountSection.tsx b/apps/desktop/src/renderer/pages/settings/sections/AccountSection.tsx index ae981aef..4bd3cfc5 100644 --- a/apps/desktop/src/renderer/pages/settings/sections/AccountSection.tsx +++ b/apps/desktop/src/renderer/pages/settings/sections/AccountSection.tsx @@ -17,8 +17,18 @@ import { ChevronRight, } from 'lucide-react'; import { getProductConfig } from '@readied/product-config'; -import { useAuthStore } from '../../../stores/authStore'; -import { useSyncStore } from '../../../stores/syncStore'; +import { + useAuthStore, + selectUser, + selectIsAuthenticated, + selectIsLoading, +} from '../../../stores/authStore'; +import { + useSyncStore, + selectStatus, + selectLastSyncAt, + selectConflicts, +} from '../../../stores/syncStore'; import { useLicense } from '../../../contexts/LicenseContext'; import { SettingGroup } from '../components/SettingGroup'; import { SettingRow } from '../components/SettingRow'; @@ -39,8 +49,15 @@ function formatBytes(bytes: number): string { } export function AccountSection() { - const { user, isAuthenticated, isLoading, logout, loadSession } = useAuthStore(); - const { syncNow, status: syncStatus, lastSyncAt, conflicts } = useSyncStore(); + const user = useAuthStore(selectUser); + const isAuthenticated = useAuthStore(selectIsAuthenticated); + const isLoading = useAuthStore(selectIsLoading); + const logout = useAuthStore(state => state.logout); + const loadSession = useAuthStore(state => state.loadSession); + const syncNow = useSyncStore(state => state.syncNow); + const syncStatus = useSyncStore(selectStatus); + const lastSyncAt = useSyncStore(selectLastSyncAt); + const conflicts = useSyncStore(selectConflicts); const { state: licenseState, openSubscribe } = useLicense(); const [showMagicLinkFlow, setShowMagicLinkFlow] = useState(false); const [message, setMessage] = useState(null); diff --git a/apps/desktop/src/renderer/plugins/tables.tsx b/apps/desktop/src/renderer/plugins/tables.tsx index 701d7d6d..054a8b54 100644 --- a/apps/desktop/src/renderer/plugins/tables.tsx +++ b/apps/desktop/src/renderer/plugins/tables.tsx @@ -1,13 +1,6 @@ import { useState, useCallback, useMemo, type ReactElement } from 'react'; -import { - ViewPlugin, - WidgetType, - Decoration, - type ViewUpdate, - type DecorationSet, - type EditorView, -} from '@codemirror/view'; -import { RangeSetBuilder } from '@codemirror/state'; +import { WidgetType, Decoration, EditorView, type DecorationSet } from '@codemirror/view'; +import { RangeSetBuilder, StateField, type EditorState } from '@codemirror/state'; import type { PluginManifest, ZoneComponentProps } from '@readied/plugin-api'; import React from 'react'; @@ -266,28 +259,23 @@ class TableWidget extends WidgetType { } } -function buildTableDecorations(view: EditorView): DecorationSet { +// Build table decorations from EditorState (StateField-compatible). +// We MUST use StateField, not ViewPlugin: tables span multiple lines, and +// CodeMirror forbids Decoration.replace() ranges that include line breaks +// when provided by a ViewPlugin. See dev.to/marijn — "Decorations that +// replace line breaks may not be specified via plugins". +function buildTableDecorations(state: EditorState): DecorationSet { const builder = new RangeSetBuilder(); - const doc = view.state.doc; + const doc = state.doc; const docText = doc.toString(); const ranges = findTableRanges(docText); - const sel = view.state.selection.main; + const sel = state.selection.main; for (const range of ranges) { // Skip if cursor is inside this table range (show raw markdown for editing) if (sel.from >= range.from && sel.from <= range.to) continue; if (sel.to >= range.from && sel.to <= range.to) continue; - // Only process tables in visible ranges - let visible = false; - for (const vr of view.visibleRanges) { - if (range.from <= vr.to && range.to >= vr.from) { - visible = true; - break; - } - } - if (!visible) continue; - const parsed = parseGfmTable(range.text, range.from); if (!parsed) continue; @@ -298,24 +286,18 @@ function buildTableDecorations(view: EditorView): DecorationSet { return builder.finish(); } -const tableViewPlugin = ViewPlugin.fromClass( - class { - decorations: DecorationSet; - - constructor(view: EditorView) { - this.decorations = buildTableDecorations(view); - } - - update(update: ViewUpdate) { - if (update.docChanged || update.selectionSet || update.viewportChanged) { - this.decorations = buildTableDecorations(update.view); - } +const tableDecorationsField = StateField.define({ + create(state) { + return buildTableDecorations(state); + }, + update(decorations, tr) { + if (tr.docChanged || tr.selection) { + return buildTableDecorations(tr.state); } + return decorations.map(tr.changes); }, - { - decorations: v => v.decorations, - } -); + provide: f => EditorView.decorations.from(f), +}); // ============================================================ // Feature 3: Sortable Preview Table (React component) @@ -493,7 +475,7 @@ export const tablesPlugin: PluginManifest = { // --- Feature 2: WYSIWYG toggle --- const enableWysiwyg = () => { if (unregisterWysiwyg) return; - unregisterWysiwyg = context.registerExtensions('table-wysiwyg', [tableViewPlugin]); + unregisterWysiwyg = context.registerExtensions('table-wysiwyg', [tableDecorationsField]); context.log.info('Table WYSIWYG enabled'); }; diff --git a/apps/desktop/src/renderer/settings.tsx b/apps/desktop/src/renderer/settings.tsx deleted file mode 100644 index 98328021..00000000 --- a/apps/desktop/src/renderer/settings.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { SettingsApp } from './pages/settings/SettingsApp'; -import { LicenseProvider } from './contexts/LicenseContext'; -import './styles/global.css'; - -class SettingsErrorBoundary extends React.Component< - { children: React.ReactNode }, - { error: Error | null } -> { - state: { error: Error | null } = { error: null }; - - static getDerivedStateFromError(error: Error) { - return { error }; - } - - render() { - if (this.state.error) { - return ( -
-

Settings failed to load

-
-            {this.state.error.message}
-          
-
-            {this.state.error.stack}
-          
-
- ); - } - return this.props.children; - } -} - -// Create QueryClient for TanStack Query -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 1000 * 60, // 1 minute - retry: 1, - }, - }, -}); - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - - - - - -); diff --git a/apps/desktop/src/renderer/tsconfig.json b/apps/desktop/src/renderer/tsconfig.json index a02455d6..29355d0e 100644 --- a/apps/desktop/src/renderer/tsconfig.json +++ b/apps/desktop/src/renderer/tsconfig.json @@ -4,6 +4,7 @@ "module": "ESNext", "moduleResolution": "bundler", "outDir": "../../out/renderer", + "rootDir": "..", "jsx": "react-jsx", "lib": ["DOM", "DOM.Iterable", "ESNext"], "noEmit": true, @@ -11,5 +12,5 @@ "@/*": ["./*"] } }, - "include": ["**/*.ts", "**/*.tsx", "**/*.d.ts", "../preload/index.ts"] + "include": ["**/*.ts", "**/*.tsx", "**/*.d.ts", "../preload/index.ts", "../preload/api/**/*.ts"] } diff --git a/apps/desktop/src/renderer/ui/patterns/.gitkeep b/apps/desktop/src/renderer/ui/patterns/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/desktop/src/renderer/ui/patterns/Modal.module.css b/apps/desktop/src/renderer/ui/patterns/Modal.module.css deleted file mode 100644 index 2b0a855e..00000000 --- a/apps/desktop/src/renderer/ui/patterns/Modal.module.css +++ /dev/null @@ -1,122 +0,0 @@ -/* ============================================================================= - Modal Pattern - Glass-effect modal with overlay, scale animation, and portal rendering. - ============================================================================= */ - -/* ── Overlay ─────────────────────────────────────────────────────────────── */ - -.overlay { - position: fixed; - inset: 0; - z-index: 1000; - display: flex; - align-items: center; - justify-content: center; - background: rgba(0, 0, 0, 0.5); - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); - animation: fade-in var(--transition-normal) ease both; -} - -/* ── Content ─────────────────────────────────────────────────────────────── */ - -.content { - position: relative; - width: 100%; - max-height: calc(100vh - 80px); - overflow-y: auto; - background: var(--glass-bg); - backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); - -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)); - border: 1px solid var(--glass-border); - border-radius: var(--radius-xl); - box-shadow: var(--glass-shadow); - animation: scale-in var(--transition-normal) ease both; -} - -/* ── Sizes ───────────────────────────────────────────────────────────────── */ - -.sm { - max-width: 360px; -} - -.md { - max-width: 480px; -} - -.lg { - max-width: 640px; -} - -/* ── Header ──────────────────────────────────────────────────────────────── */ - -.header { - display: flex; - align-items: center; - justify-content: space-between; - padding: var(--space-4) var(--space-5); - border-bottom: 1px solid var(--border-subtle); -} - -.title { - margin: 0; - font-family: var(--font-sans); - font-size: var(--text-lg); - font-weight: var(--font-weight-semibold); - line-height: var(--leading-tight); - letter-spacing: var(--tracking-tight); - color: var(--text-primary); -} - -.closeButton { - display: flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - padding: 0; - margin: 0; - background: transparent; - border: none; - border-radius: var(--radius-md); - color: var(--text-muted); - cursor: pointer; - transition: background var(--transition-fast), color var(--transition-fast); -} - -.closeButton:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.closeButton:active { - background: var(--bg-active); -} - -/* ── Body ────────────────────────────────────────────────────────────────── */ - -.body { - padding: var(--space-5); -} - -/* ── Animations ──────────────────────────────────────────────────────────── */ - -@keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes scale-in { - from { - opacity: 0; - transform: scale(0.95); - } - to { - opacity: 1; - transform: scale(1); - } -} diff --git a/apps/desktop/src/renderer/ui/patterns/Modal.tsx b/apps/desktop/src/renderer/ui/patterns/Modal.tsx deleted file mode 100644 index 6f587cf8..00000000 --- a/apps/desktop/src/renderer/ui/patterns/Modal.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { useCallback, useEffect, useId, useRef, type ReactNode } from 'react'; -import { createPortal } from 'react-dom'; -import styles from './Modal.module.css'; - -export interface ModalProps { - open: boolean; - onClose: () => void; - title?: string; - children: ReactNode; - size?: 'sm' | 'md' | 'lg'; - closeOnOverlay?: boolean; - closeOnEscape?: boolean; -} - -export function Modal({ - open, - onClose, - title, - children, - size = 'md', - closeOnOverlay = true, - closeOnEscape = true, -}: ModalProps) { - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (closeOnEscape && e.key === 'Escape') { - onClose(); - } - }, - [closeOnEscape, onClose] - ); - - useEffect(() => { - if (!open) return; - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, [open, handleKeyDown]); - - const handleOverlayClick = useCallback( - (e: React.MouseEvent) => { - if (closeOnOverlay && e.target === e.currentTarget) { - onClose(); - } - }, - [closeOnOverlay, onClose] - ); - - const contentRef = useRef(null); - const generatedId = useId(); - - // Focus the modal container on open - useEffect(() => { - if (open && contentRef.current) { - contentRef.current.focus(); - } - }, [open]); - - if (!open) return null; - - const titleId = title != null ? generatedId : undefined; - - return createPortal( -
-
- {title != null && ( -
-

- {title} -

- -
- )} -
{children}
-
-
, - document.body - ); -} diff --git a/apps/desktop/src/renderer/ui/patterns/index.ts b/apps/desktop/src/renderer/ui/patterns/index.ts deleted file mode 100644 index 05844a90..00000000 --- a/apps/desktop/src/renderer/ui/patterns/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Modal } from './Modal'; -export type { ModalProps } from './Modal'; diff --git a/apps/desktop/vitest.config.ts b/apps/desktop/vitest.config.ts index d74d4049..d83fca40 100644 --- a/apps/desktop/vitest.config.ts +++ b/apps/desktop/vitest.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: true, environment: 'node', include: ['src/**/__tests__/**/*.test.ts'], + coverage: sharedCoverage, }, }); diff --git a/apps/web/package.json b/apps/web/package.json index 6b1ccc78..1b71351a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -8,35 +8,35 @@ "start": "next start" }, "dependencies": { - "@radix-ui/react-accordion": "^1.2.12", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-accordion": "^1.2.13", + "@radix-ui/react-dialog": "^1.1.16", + "@radix-ui/react-separator": "^1.1.9", + "@radix-ui/react-slot": "^1.2.5", "@readied/product-config": "workspace:*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "framer-motion": "^12.38.0", - "fumadocs-core": "^16.8.2", - "fumadocs-mdx": "^14.3.1", - "fumadocs-ui": "^16.8.2", - "lucide-react": "^1.8.0", - "marked": "^18.0.2", - "next": "^16.2.6", + "framer-motion": "^12.40.0", + "fumadocs-core": "^16.9.3", + "fumadocs-mdx": "^15.0.11", + "fumadocs-ui": "^16.9.3", + "lucide-react": "^1.17.0", + "marked": "^18.0.5", + "next": "^16.2.7", "next-themes": "^0.4.6", - "react": "^19.2.5", - "react-dom": "^19.2.5", - "tailwind-merge": "^3.5.0", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "tailwind-merge": "^3.6.0", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@fontsource-variable/jetbrains-mono": "^5.2.8", "@fontsource/inter": "^5.2.8", - "@tailwindcss/postcss": "^4.2.4", - "@types/node": "25.4.0", - "@types/react": "^19.2.14", + "@tailwindcss/postcss": "^4.3.0", + "@types/node": "25.9.2", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", - "postcss": "^8.5.10", - "tailwindcss": "^4.2.4", - "typescript": "^5.7.0" + "postcss": "^8.5.15", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3" } } diff --git a/knip.json b/knip.json new file mode 100644 index 00000000..24b39654 --- /dev/null +++ b/knip.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://unpkg.com/knip@5/schema.json", + "workspaces": { + ".": { + "entry": ["eslint.config.js"], + "ignoreDependencies": [ + "@semantic-release/changelog", + "@semantic-release/commit-analyzer", + "@semantic-release/exec", + "@semantic-release/git", + "@semantic-release/github", + "@semantic-release/release-notes-generator", + "conventional-changelog-conventionalcommits", + "semantic-release" + ] + }, + "apps/desktop": { + "entry": [ + "src/main/index.ts", + "src/preload/index.ts", + "src/renderer/main.tsx", + "electron-vite.config.ts", + "vitest.config.ts", + "playwright.config.ts", + "e2e/**/*.{ts,spec.ts}" + ] + }, + "apps/web": { + "entry": ["src/**/*.{ts,tsx,astro}", "astro.config.{ts,js,mjs}"] + }, + "packages/*": { + "entry": ["src/index.ts"] + } + } +} diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 00000000..66845786 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,20 @@ +# Lefthook config — replaces husky. +# Install: `pnpm install` triggers postinstall → `lefthook install`. +# Skip a single run with LEFTHOOK=0 git commit ... + +pre-commit: + parallel: true + commands: + lint-staged: + run: pnpm lint-staged + stage_fixed: true + +pre-push: + commands: + typecheck: + run: pnpm -r typecheck + +commit-msg: + commands: + commitlint: + run: pnpm commitlint --edit {1} diff --git a/package.json b/package.json index dea3bec7..1c4d7b5a 100644 --- a/package.json +++ b/package.json @@ -31,38 +31,46 @@ "dev": "turbo dev", "build": "turbo build", "test": "turbo test --filter=!@readied/storage-sqlite", + "test:coverage": "turbo test --filter=!@readied/storage-sqlite -- --coverage", "lint": "eslint packages apps --cache", "lint:fix": "eslint packages apps --fix --cache", "format": "prettier --write \"**/*.{ts,tsx,js,json,md}\" --ignore-path .gitignore", "format:check": "prettier --check \"**/*.{ts,tsx,js,json,md}\" --ignore-path .gitignore", "typecheck": "turbo typecheck", "clean": "turbo clean && rm -rf node_modules .eslintcache", - "prepare": "husky" + "knip": "knip", + "postinstall": "lefthook install" }, "devDependencies": { - "@commitlint/cli": "^20.5.0", - "@commitlint/config-conventional": "^20.5.0", + "@commitlint/cli": "^21.0.2", + "@commitlint/config-conventional": "^21.0.2", "@eslint/js": "^10.0.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/exec": "^7.1.0", "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^12.0.6", - "@semantic-release/release-notes-generator": "^14.1.0", + "@semantic-release/github": "^12.0.8", + "@semantic-release/release-notes-generator": "^14.1.1", + "@vitest/coverage-v8": "^4.1.8", "conventional-changelog-conventionalcommits": "^9.3.1", - "eslint": "^9.39.2", - "eslint-plugin-import-x": "^4.16.1", - "husky": "^9.1.7", - "lint-staged": "^16.4.0", - "prettier": "^3.7.4", + "eslint": "^10.4.1", + "eslint-plugin-import-x": "^4.16.2", + "knip": "^5.66.0", + "lefthook": "^1.13.6", + "lint-staged": "^17.0.7", + "prettier": "^3.8.3", "semantic-release": "^25.0.3", - "turbo": "^2.9.14", - "typescript": "^5.7.2", - "typescript-eslint": "^8.59.0", - "vitest": "^4.1.0" + "turbo": "^2.9.16", + "typescript": "^6.0.3", + "typescript-eslint": "^8.60.1", + "vitest": "^4.1.8" }, "lint-staged": { - "*.{ts,tsx,js,json,md}": "prettier --write" + "*.{ts,tsx,js}": [ + "eslint --cache --fix --max-warnings 0", + "prettier --write" + ], + "*.{json,md}": "prettier --write" }, "packageManager": "pnpm@9.15.1", "pnpm": {}, diff --git a/packages/ai-core/package.json b/packages/ai-core/package.json index 706d127f..f50f9345 100644 --- a/packages/ai-core/package.json +++ b/packages/ai-core/package.json @@ -18,8 +18,8 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/api/.dev.vars b/packages/api/.dev.vars index 08f21ae5..1e97894f 100644 --- a/packages/api/.dev.vars +++ b/packages/api/.dev.vars @@ -17,5 +17,12 @@ RESEND_API_KEY="re_your_key_here" # Stripe Webhook Secret (get from: https://dashboard.stripe.com/webhooks) STRIPE_WEBHOOK_SECRET="whsec_your_secret_here" +# Ed25519 private key used to sign SignedSubscriptionEnvelope payloads. +# Generate locally with: +# cd packages/licensing && node -e "import('@noble/ed25519').then(async (ed) => { const sk = ed.utils.randomSecretKey ? ed.utils.randomSecretKey() : ed.utils.randomPrivateKey(); const pk = await ed.getPublicKeyAsync(sk); const hex = (b) => Array.from(b).map(x => x.toString(16).padStart(2,'0')).join(''); console.log('PUBLIC:', hex(pk), '\nPRIVATE:', hex(sk)); });" +# Put the PUBLIC in apps/desktop/src/main/services/fileLicenseStorage.ts > SUBSCRIPTION_PUBLIC_KEY +# Put the PRIVATE here (and as the Cloudflare secret in production/staging) +LICENSE_SIGNING_PRIVATE_KEY="hex-encoded-32-byte-ed25519-private-key" + # Environment ENVIRONMENT="development" diff --git a/packages/api/package.json b/packages/api/package.json index 24404efa..8569ba7f 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -27,19 +27,19 @@ "test:watch": "vitest" }, "dependencies": { - "@hono/zod-validator": "^0.7.6", + "@hono/zod-validator": "^0.8.0", "@libsql/client": "^0.17.3", "drizzle-orm": "^0.45.2", - "hono": "^4.12.21", - "jose": "^6.2.2", - "stripe": "^22.0.2", - "zod": "^4.3.6" + "hono": "^4.12.23", + "jose": "^6.2.3", + "stripe": "^22.2.0", + "zod": "^4.4.3" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20260423.1", + "@cloudflare/workers-types": "^4.20260608.1", "drizzle-kit": "^0.31.10", - "typescript": "^5.7.2", - "vitest": "^4.1.0", - "wrangler": "^4.84.1" + "typescript": "^6.0.3", + "vitest": "^4.1.8", + "wrangler": "^4.98.0" } } diff --git a/packages/api/vitest.config.ts b/packages/api/vitest.config.ts index 7382f40e..10995c53 100644 --- a/packages/api/vitest.config.ts +++ b/packages/api/vitest.config.ts @@ -1,7 +1,9 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: true, + coverage: sharedCoverage, }, }); diff --git a/packages/api/wrangler.toml b/packages/api/wrangler.toml index 19deaab9..c93ac1f4 100644 --- a/packages/api/wrangler.toml +++ b/packages/api/wrangler.toml @@ -27,3 +27,8 @@ vars = { ENVIRONMENT = "production" } # JWT_SECRET - Secret for signing JWTs (generate with: openssl rand -base64 32) # RESEND_API_KEY - API key for Resend email service # STRIPE_WEBHOOK_SECRET - Stripe webhook signing secret +# LICENSE_SIGNING_PRIVATE_KEY - Ed25519 private key (hex) used to sign +# SignedSubscriptionEnvelope payloads. The matching public key is +# embedded in apps/desktop/src/main/services/fileLicenseStorage.ts +# (SUBSCRIPTION_PUBLIC_KEY). Rotation requires shipping a new desktop +# release with the new public key FIRST — see that file's comment. diff --git a/packages/command-registry/package.json b/packages/command-registry/package.json index 8a41906b..f0d5df0c 100644 --- a/packages/command-registry/package.json +++ b/packages/command-registry/package.json @@ -22,8 +22,8 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/commands/package.json b/packages/commands/package.json index 93a62558..e08b02f6 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -25,9 +25,9 @@ "devDependencies": { "@codemirror/commands": "^6.10.3", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.41.1", - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "@codemirror/view": "^6.43.0", + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/commands/src/markdown/commands.test.ts b/packages/commands/src/markdown/commands.test.ts new file mode 100644 index 00000000..808202bc --- /dev/null +++ b/packages/commands/src/markdown/commands.test.ts @@ -0,0 +1,128 @@ +import { describe, it, expect, vi } from 'vitest'; +import { EditorState, EditorSelection } from '@codemirror/state'; +import type { EditorView } from '@codemirror/view'; +import { + toggleBold, + toggleItalic, + toggleStrikethrough, + toggleInlineCode, + insertHeading, + insertUnorderedList, + insertOrderedList, + insertCheckbox, + insertQuote, + insertCodeBlock, + insertHorizontalRule, +} from './commands.js'; + +// CodeMirror commands need an EditorView to call view.dispatch + view.focus. +// We fake one with just the surface area the command functions touch — no DOM. +function fakeView(initialDoc: string, selection: { from: number; to: number }) { + let state = EditorState.create({ + doc: initialDoc, + selection: EditorSelection.range(selection.from, selection.to), + }); + const focus = vi.fn(); + const dispatch = vi.fn((spec: Parameters[0]) => { + state = state.update(spec).state; + }); + const view = { + get state() { + return state; + }, + dispatch, + focus, + }; + return { + view: view as unknown as EditorView, + doc: () => state.doc.toString(), + selectionMain: () => state.selection.main, + dispatchCalls: () => dispatch.mock.calls.length, + }; +} + +describe('@readied/commands markdown', () => { + describe('wrapping commands', () => { + it('toggleBold wraps selected text with **', () => { + const t = fakeView('hello world', { from: 0, to: 5 }); + toggleBold(t.view); + expect(t.doc()).toBe('**hello** world'); + }); + + it('toggleBold unwraps when applied to already-bold text', () => { + const t = fakeView('**hello** world', { from: 2, to: 7 }); + toggleBold(t.view); + expect(t.doc()).toBe('hello world'); + }); + + it('toggleItalic wraps with single asterisks', () => { + const t = fakeView('hello', { from: 0, to: 5 }); + toggleItalic(t.view); + expect(t.doc()).toBe('*hello*'); + }); + + it('toggleStrikethrough wraps with ~~', () => { + const t = fakeView('hello', { from: 0, to: 5 }); + toggleStrikethrough(t.view); + expect(t.doc()).toBe('~~hello~~'); + }); + + it('toggleInlineCode wraps with backticks', () => { + const t = fakeView('hello', { from: 0, to: 5 }); + toggleInlineCode(t.view); + expect(t.doc()).toBe('`hello`'); + }); + }); + + describe('line-prefix commands', () => { + it('insertHeading prepends ## by default (level 2)', () => { + const t = fakeView('title', { from: 0, to: 0 }); + insertHeading(t.view); + expect(t.doc()).toBe('## title'); + }); + + it('insertHeading respects explicit level', () => { + const t = fakeView('title', { from: 0, to: 0 }); + insertHeading(t.view, 4); + expect(t.doc()).toBe('#### title'); + }); + + it('insertUnorderedList prepends - to the line', () => { + const t = fakeView('item', { from: 0, to: 0 }); + insertUnorderedList(t.view); + expect(t.doc()).toBe('- item'); + }); + + it('insertOrderedList prepends 1. to the line', () => { + const t = fakeView('item', { from: 0, to: 0 }); + insertOrderedList(t.view); + expect(t.doc()).toBe('1. item'); + }); + + it('insertCheckbox prepends - [ ] to the line', () => { + const t = fakeView('task', { from: 0, to: 0 }); + insertCheckbox(t.view); + expect(t.doc()).toBe('- [ ] task'); + }); + + it('insertQuote prepends > to the line', () => { + const t = fakeView('quoted', { from: 0, to: 0 }); + insertQuote(t.view); + expect(t.doc()).toBe('> quoted'); + }); + }); + + describe('block-insertion commands', () => { + it('insertCodeBlock inserts a fenced code block', () => { + const t = fakeView('', { from: 0, to: 0 }); + insertCodeBlock(t.view); + expect(t.doc()).toContain('```'); + }); + + it('insertHorizontalRule inserts a markdown rule', () => { + const t = fakeView('', { from: 0, to: 0 }); + insertHorizontalRule(t.view); + expect(t.doc()).toContain('---'); + }); + }); +}); diff --git a/packages/commands/vitest.config.ts b/packages/commands/vitest.config.ts index b6aa913f..058ad3a5 100644 --- a/packages/commands/vitest.config.ts +++ b/packages/commands/vitest.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: false, environment: 'node', passWithNoTests: true, + coverage: sharedCoverage, }, }); diff --git a/packages/core/package.json b/packages/core/package.json index b803842b..a01ba6fd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -18,11 +18,11 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "zod": "^4.3.6" + "zod": "^4.4.3" }, "devDependencies": { - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index 8996a048..edde6c9d 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: true, environment: 'node', include: ['tests/**/*.test.ts'], + coverage: sharedCoverage, }, }); diff --git a/packages/embeds/package.json b/packages/embeds/package.json index 975312ba..6560a1f9 100644 --- a/packages/embeds/package.json +++ b/packages/embeds/package.json @@ -38,10 +38,10 @@ }, "devDependencies": { "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.41.1", + "@codemirror/view": "^6.43.0", "@types/mdast": "^4.0.4", - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/embeds/vitest.config.ts b/packages/embeds/vitest.config.ts index 2dcea8c5..c2a23743 100644 --- a/packages/embeds/vitest.config.ts +++ b/packages/embeds/vitest.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: false, environment: 'node', + coverage: sharedCoverage, }, }); diff --git a/packages/licensing/README.md b/packages/licensing/README.md new file mode 100644 index 00000000..1f8bac37 --- /dev/null +++ b/packages/licensing/README.md @@ -0,0 +1,86 @@ +# @readied/licensing + +License and subscription verification helpers. + +## Subscription envelope (Ed25519) + +The desktop app caches its subscription state on disk. To prevent users from editing that file to grant themselves a paid plan, the **server signs every subscription payload with an Ed25519 private key**. The desktop verifies with an embedded public key. There is **no shared secret** on the client. + +### Wire format + +The server returns (and the client persists) a `SignedSubscriptionEnvelope`: + +```ts +{ + payload: { + payloadVersion: 1, + subscription: { /* SubscriptionInfo — id, customer, plan, status, period... */ }, + issuedAt: "2026-06-08T12:00:00.000Z", // when the server signed this + ttlSeconds?: 3600 // optional max age the server wants the client to honour + }, + signature: "" +} +``` + +The signature is computed over `canonicalJson(payload)` — a deterministic, sorted-key JSON encoding (see `canonicalJson` in `validator.ts`). Both sides MUST canonicalize identically, otherwise verification will fail even when the data is unchanged. + +### Server side (signing) + +```ts +import { signSubscriptionPayload } from '@readied/licensing'; + +const envelope = await signSubscriptionPayload( + { + payloadVersion: 1, + subscription: subscriptionInfoFromStripe, + issuedAt: new Date().toISOString(), + ttlSeconds: 3600, // optional + }, + process.env.LICENSE_SIGNING_PRIVATE_KEY! // 32-byte Ed25519 private key, hex +); + +return envelope; +``` + +- The private key MUST live only on the server. Never commit it. Rotate by generating a new keypair (`generateKeyPair`), updating the embedded public key in the desktop, and shipping a new release. +- `issuedAt` is mandatory — replay protection on the client uses it. + +### Client side (verification) + +```ts +import { verifySubscriptionSignature } from '@readied/licensing'; + +const result = await verifySubscriptionSignature(envelope, { + publicKey: SUBSCRIPTION_PUBLIC_KEY, // embedded in the desktop + // maxAgeSeconds: 24 * 3600, // optional; otherwise honours ttlSeconds +}); + +if (!result.valid) { + // Treat as not-subscribed. Log result.error. +} +``` + +### Embedded public key + +`DEFAULT_SUBSCRIPTION_PUBLIC_KEY` in `validator.ts` is a **placeholder** (`0000…`). It MUST be replaced with the actual server public key before shipping signed subscriptions. Callers may also pass `config.publicKey` explicitly, which is the form used by every internal consumer. + +### Replay & clock skew + +`verifySubscriptionSignature` rejects: + +- Envelopes older than `maxAgeSeconds` (defaults to `payload.ttlSeconds`, otherwise 7 days). +- Envelopes whose `issuedAt` is more than 60 seconds in the future (tolerates small clock skew between client and server). + +### What is NOT signed + +- **Trial state** (`trial.json`) is created entirely on the client when the user first starts a trial. There is no server-side trial registration. A determined user can extend their trial by editing the file. This is accepted: the trial is best-effort and the goal is to deter casual tampering, not stop a motivated attacker. Subscription is the real boundary. +- The **legacy license file** (`LicenseFile`) has its own signature scheme via `validateLicense` / `signLicense`, kept for backwards compatibility while the subscription model phases it out. + +## Rolling the signing key + +1. Generate a new keypair with `generateKeyPair()`. +2. Ship a desktop release with the new public key embedded. +3. Once enough clients have updated, switch the server to sign with the new private key. +4. Old clients with the previous public key will fail verification and treat users as not-subscribed until they update. + +Plan windowed rollouts accordingly — there is no client-side multi-key acceptance today. diff --git a/packages/licensing/__tests__/subscriptionSignature.test.ts b/packages/licensing/__tests__/subscriptionSignature.test.ts new file mode 100644 index 00000000..428a4a5f --- /dev/null +++ b/packages/licensing/__tests__/subscriptionSignature.test.ts @@ -0,0 +1,195 @@ +import { describe, it, expect } from 'vitest'; +import { + canonicalJson, + signSubscriptionPayload, + verifySubscriptionSignature, + generateKeyPair, +} from '../src/validator.js'; +import type { SignedSubscriptionPayload, SubscriptionInfo } from '../src/types.js'; + +const futureIso = (offsetMs: number): string => new Date(Date.now() + offsetMs).toISOString(); + +function makeSubscription(overrides: Partial = {}): SubscriptionInfo { + return { + subscriptionId: 'sub_test_abc', + customerId: 'cus_test_xyz', + email: 'user@example.com', + plan: 'monthly', + status: 'active', + currentPeriodStart: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), + currentPeriodEnd: futureIso(30 * 24 * 60 * 60 * 1000), + cancelAtPeriodEnd: false, + ...overrides, + }; +} + +function makePayload( + overrides: Partial = {} +): SignedSubscriptionPayload { + return { + payloadVersion: 1, + subscription: makeSubscription(), + issuedAt: new Date().toISOString(), + ...overrides, + }; +} + +describe('canonicalJson', () => { + it('emits sorted-key JSON regardless of insertion order', () => { + const a = canonicalJson({ b: 2, a: 1 }); + const b = canonicalJson({ a: 1, b: 2 }); + expect(a).toBe(b); + expect(a).toBe('{"a":1,"b":2}'); + }); + + it('recurses into nested objects', () => { + const a = canonicalJson({ outer: { z: 1, a: 2 } }); + expect(a).toBe('{"outer":{"a":2,"z":1}}'); + }); + + it('keeps arrays in their original order', () => { + expect(canonicalJson([3, 1, 2])).toBe('[3,1,2]'); + }); + + it('drops undefined fields like JSON.stringify does', () => { + expect(canonicalJson({ a: 1, b: undefined })).toBe('{"a":1}'); + }); + + it('handles primitives', () => { + expect(canonicalJson(null)).toBe('null'); + expect(canonicalJson(42)).toBe('42'); + expect(canonicalJson('x')).toBe('"x"'); + expect(canonicalJson(true)).toBe('true'); + }); +}); + +describe('signSubscriptionPayload + verifySubscriptionSignature', () => { + it('sign then verify round-trips with a fresh keypair', async () => { + const keys = await generateKeyPair(); + const payload = makePayload(); + const envelope = await signSubscriptionPayload(payload, keys.privateKey); + + expect(envelope.signature).toBeTruthy(); + expect(envelope.payload).toEqual(payload); + + const result = await verifySubscriptionSignature(envelope, { publicKey: keys.publicKey }); + expect(result.valid).toBe(true); + expect(result.subscription).toEqual(payload.subscription); + }); + + it('rejects an envelope signed with a different key', async () => { + const signer = await generateKeyPair(); + const other = await generateKeyPair(); + const envelope = await signSubscriptionPayload(makePayload(), signer.privateKey); + + const result = await verifySubscriptionSignature(envelope, { publicKey: other.publicKey }); + expect(result.valid).toBe(false); + expect(result.error).toMatch(/Signature verification failed/); + }); + + it('rejects a tampered payload', async () => { + const keys = await generateKeyPair(); + const envelope = await signSubscriptionPayload(makePayload(), keys.privateKey); + + const tampered = { + ...envelope, + payload: { + ...envelope.payload, + subscription: { + ...envelope.payload.subscription, + email: 'attacker@example.com', + }, + }, + }; + + const result = await verifySubscriptionSignature(tampered, { publicKey: keys.publicKey }); + expect(result.valid).toBe(false); + }); + + it('rejects unsupported payloadVersion', async () => { + const keys = await generateKeyPair(); + const envelope = await signSubscriptionPayload( + // @ts-expect-error — intentionally bad version + makePayload({ payloadVersion: 99 }), + keys.privateKey + ); + const result = await verifySubscriptionSignature(envelope, { publicKey: keys.publicKey }); + expect(result.valid).toBe(false); + expect(result.error).toMatch(/payload version/i); + }); + + it('rejects an envelope older than maxAgeSeconds', async () => { + const keys = await generateKeyPair(); + const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); + const envelope = await signSubscriptionPayload( + makePayload({ issuedAt: sevenDaysAgo }), + keys.privateKey + ); + const result = await verifySubscriptionSignature(envelope, { + publicKey: keys.publicKey, + maxAgeSeconds: 60 * 60, // 1 hour + }); + expect(result.valid).toBe(false); + expect(result.error).toMatch(/older than max age/); + }); + + it('honours per-envelope ttlSeconds when caller does not override', async () => { + const keys = await generateKeyPair(); + const issuedAt = new Date(Date.now() - 10 * 1000).toISOString(); + const envelope = await signSubscriptionPayload( + makePayload({ issuedAt, ttlSeconds: 5 }), + keys.privateKey + ); + const result = await verifySubscriptionSignature(envelope, { publicKey: keys.publicKey }); + expect(result.valid).toBe(false); + expect(result.error).toMatch(/older than max age/); + }); + + it('rejects envelopes timestamped far in the future', async () => { + const keys = await generateKeyPair(); + const inFiveMinutes = new Date(Date.now() + 5 * 60 * 1000).toISOString(); + const envelope = await signSubscriptionPayload( + makePayload({ issuedAt: inFiveMinutes }), + keys.privateKey + ); + const result = await verifySubscriptionSignature(envelope, { publicKey: keys.publicKey }); + expect(result.valid).toBe(false); + expect(result.error).toMatch(/from the future/); + }); + + it('uses the injectable clock to make timing tests deterministic', async () => { + const keys = await generateKeyPair(); + const issuedAt = '2026-01-01T00:00:00.000Z'; + const envelope = await signSubscriptionPayload(makePayload({ issuedAt }), keys.privateKey); + const result = await verifySubscriptionSignature(envelope, { + publicKey: keys.publicKey, + // Set the clock 30 seconds after issuedAt — well inside any sensible TTL. + nowMs: new Date(issuedAt).getTime() + 30 * 1000, + }); + expect(result.valid).toBe(true); + }); + + it('rejects an inactive subscription even with a valid signature', async () => { + const keys = await generateKeyPair(); + const envelope = await signSubscriptionPayload( + makePayload({ + subscription: makeSubscription({ + status: 'canceled', + currentPeriodEnd: new Date(Date.now() - 1).toISOString(), + }), + }), + keys.privateKey + ); + const result = await verifySubscriptionSignature(envelope, { publicKey: keys.publicKey }); + expect(result.valid).toBe(false); + }); + + it('rejects envelopes missing required fields', async () => { + expect((await verifySubscriptionSignature(null)).valid).toBe(false); + expect((await verifySubscriptionSignature({})).valid).toBe(false); + expect((await verifySubscriptionSignature({ payload: makePayload() })).valid).toBe(false); + expect((await verifySubscriptionSignature({ signature: 'x', payload: null })).valid).toBe( + false + ); + }); +}); diff --git a/packages/licensing/package.json b/packages/licensing/package.json index 1ee99692..463ddc25 100644 --- a/packages/licensing/package.json +++ b/packages/licensing/package.json @@ -22,8 +22,8 @@ "@readied/product-config": "workspace:*" }, "devDependencies": { - "@types/node": "^22.10.2", - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "@types/node": "^25.9.2", + "typescript": "^6.0.3", + "vitest": "^4.1.8" } } diff --git a/packages/licensing/src/index.ts b/packages/licensing/src/index.ts index 3bf35f06..bcd611ee 100644 --- a/packages/licensing/src/index.ts +++ b/packages/licensing/src/index.ts @@ -9,6 +9,8 @@ export type { StoredTrialData, StoredSubscriptionData, VerificationResult, + SignedSubscriptionPayload, + SignedSubscriptionEnvelope, // Legacy types (deprecated) LicenseFile, ActiveLicense, @@ -26,6 +28,9 @@ export { isCachedSubscriptionValid, createStoredSubscription, verifySubscription, + canonicalJson, + signSubscriptionPayload, + verifySubscriptionSignature, } from './validator.js'; // Trial diff --git a/packages/licensing/src/types.ts b/packages/licensing/src/types.ts index a9976ecc..f9635a31 100644 --- a/packages/licensing/src/types.ts +++ b/packages/licensing/src/types.ts @@ -75,12 +75,20 @@ export interface StoredTrialData { } /** - * Stored subscription data (cached locally) + * Stored subscription data (cached locally). + * + * `signedEnvelope` is the server-signed source of truth when present. + * `subscription` is the unsigned view derived from it (or, during the + * migration period before the server emits signed envelopes, the raw + * API response). Clients that have an envelope MUST verify it before + * trusting the cached subscription — see verifySubscriptionSignature. */ export interface StoredSubscriptionData { readonly subscription: SubscriptionInfo; readonly lastVerified: string; // ISO 8601 readonly cacheExpiresAt: string; // ISO 8601 + /** Signed envelope from the server. Optional during migration. */ + readonly signedEnvelope?: SignedSubscriptionEnvelope; } /** @@ -92,6 +100,38 @@ export interface VerificationResult { readonly subscription?: SubscriptionInfo; } +/** + * The exact payload that the server signs. + * + * Keep this stable — any change here invalidates every existing signature. + * When the schema needs to evolve, bump `payloadVersion` and let the client + * accept both versions during the transition. + */ +export interface SignedSubscriptionPayload { + readonly payloadVersion: 1; + /** The verified subscription state at the moment the server signed it. */ + readonly subscription: SubscriptionInfo; + /** When the server produced this signature (ISO 8601). */ + readonly issuedAt: string; + /** + * Optional max-age, in seconds. Lets the server tell the client how long + * to trust this signed copy before requiring a fresh fetch. + * If absent, the client applies its default policy. + */ + readonly ttlSeconds?: number; +} + +/** + * Envelope sent over the wire (and persisted on disk) — payload plus its + * Ed25519 signature. The signature is computed over a deterministic JSON + * encoding of `payload` so client and server produce identical bytes. + */ +export interface SignedSubscriptionEnvelope { + readonly payload: SignedSubscriptionPayload; + /** base64(Ed25519(canonicalJson(payload), serverPrivateKey)) */ + readonly signature: string; +} + // ============================================ // LEGACY TYPES (kept for migration, will remove) // ============================================ diff --git a/packages/licensing/src/validator.ts b/packages/licensing/src/validator.ts index 709ae293..23d5cec8 100644 --- a/packages/licensing/src/validator.ts +++ b/packages/licensing/src/validator.ts @@ -6,6 +6,8 @@ import type { VerificationResult, SubscriptionInfo, StoredSubscriptionData, + SignedSubscriptionPayload, + SignedSubscriptionEnvelope, } from './types.js'; /** @@ -14,6 +16,20 @@ import type { */ const DEFAULT_PUBLIC_KEY = '808de62a74a99bc70bf16f9df1ce3a7d6417e8d8479a6193df2bc28e6d510517'; +/** + * Default public key for subscription-envelope verification. + * + * REPLACE BEFORE SHIPPING. This is a placeholder that does NOT correspond + * to any production server key — calls to verifySubscriptionSignature + * without an explicit publicKey will fail until this constant is updated + * with the actual server public key. + * + * The matching private key MUST live only on the licensing server. Never + * commit it. + */ +const DEFAULT_SUBSCRIPTION_PUBLIC_KEY = + '0000000000000000000000000000000000000000000000000000000000000000'; + /** * Extracts the payload portion of a license for signature verification */ @@ -357,3 +373,141 @@ export function createStoredSubscription( cacheExpiresAt: cacheExpires.toISOString(), }; } + +// ============================================================================ +// Signed Subscription Envelope (Ed25519) +// ============================================================================ + +/** + * Deterministic JSON encoder used as the signed message. + * + * Ed25519 signs bytes, not concepts — so the client and server MUST + * serialize the payload identically. JSON.stringify is non-deterministic + * across runtimes when objects have different insertion orders, so we + * sort keys alphabetically at every depth before stringifying. + * + * Arrays keep their order. Numbers, strings, booleans, null are emitted + * verbatim. Functions / undefined are stripped (as in JSON.stringify). + */ +export function canonicalJson(value: unknown): string { + if (value === null || typeof value !== 'object') { + return JSON.stringify(value); + } + if (Array.isArray(value)) { + return '[' + value.map(canonicalJson).join(',') + ']'; + } + const obj = value as Record; + const sortedKeys = Object.keys(obj).sort(); + const parts = sortedKeys + .filter(k => obj[k] !== undefined) + .map(k => JSON.stringify(k) + ':' + canonicalJson(obj[k])); + return '{' + parts.join(',') + '}'; +} + +/** + * Sign a subscription payload with the server's Ed25519 private key. + * + * Intended for SERVER use only. The client never holds the private key. + * + * @param payload - The payload to sign. Should include a fresh `issuedAt`. + * @param privateKeyHex - 32-byte Ed25519 private key, hex-encoded. + * @returns The signed envelope ready to send over the wire / persist. + */ +export async function signSubscriptionPayload( + payload: SignedSubscriptionPayload, + privateKeyHex: string +): Promise { + const privateKey = hexToBytes(privateKeyHex); + const message = stringToBytes(canonicalJson(payload)); + const signature = await ed.signAsync(message, privateKey); + return { + payload, + signature: bytesToBase64(signature), + }; +} + +/** + * Verify a subscription envelope received from the server (or read from a + * local cache file). + * + * Checks performed: + * 1. Envelope shape (payload + signature present). + * 2. Payload shape (payloadVersion, issuedAt, subscription). + * 3. Ed25519 signature against the public key. + * 4. Optional age check — if `maxAgeSeconds` is given, reject payloads + * whose `issuedAt` is older than that. + * 5. Subscription's own activity window (delegates to verifySubscription). + * + * @param envelope - The signed envelope to verify. + * @param config - Optional public key + age policy + injectable clock for tests. + */ +export async function verifySubscriptionSignature( + envelope: unknown, + config?: PublicKeyConfig & { + /** Max age of the signature, in seconds. Defaults to the envelope's + * own `ttlSeconds`, then to 7 days. */ + maxAgeSeconds?: number; + /** Injectable clock for tests. Defaults to Date.now(). */ + nowMs?: number; + } +): Promise { + if (typeof envelope !== 'object' || envelope === null) { + return { valid: false, error: 'Invalid envelope: not an object' }; + } + const env = envelope as Record; + if (typeof env.signature !== 'string' || env.signature.length === 0) { + return { valid: false, error: 'Invalid envelope: missing signature' }; + } + if (typeof env.payload !== 'object' || env.payload === null) { + return { valid: false, error: 'Invalid envelope: missing payload' }; + } + + const payload = env.payload as Record; + if (payload.payloadVersion !== 1) { + return { valid: false, error: 'Unsupported payload version' }; + } + if (typeof payload.issuedAt !== 'string') { + return { valid: false, error: 'Invalid envelope: missing issuedAt' }; + } + + // Verify Ed25519 signature. + try { + const publicKeyHex = config?.publicKey ?? DEFAULT_SUBSCRIPTION_PUBLIC_KEY; + const publicKey = hexToBytes(publicKeyHex); + const signature = base64ToBytes(env.signature as string); + const message = stringToBytes(canonicalJson(env.payload)); + const ok = await ed.verifyAsync(signature, message, publicKey); + if (!ok) { + return { valid: false, error: 'Signature verification failed' }; + } + } catch { + return { valid: false, error: 'Signature verification threw' }; + } + + // Replay window. The default below applies only when neither the + // envelope nor the caller provide one. + const DEFAULT_MAX_AGE_SECONDS = 7 * 24 * 60 * 60; + const maxAgeSeconds = + config?.maxAgeSeconds ?? + (typeof payload.ttlSeconds === 'number' + ? (payload.ttlSeconds as number) + : DEFAULT_MAX_AGE_SECONDS); + const issuedAtMs = new Date(payload.issuedAt as string).getTime(); + const nowMs = config?.nowMs ?? Date.now(); + if (Number.isNaN(issuedAtMs)) { + return { valid: false, error: 'Invalid issuedAt' }; + } + if (nowMs - issuedAtMs > maxAgeSeconds * 1000) { + return { valid: false, error: 'Signed payload is older than max age' }; + } + if (issuedAtMs - nowMs > 60 * 1000) { + // Allow 60s clock skew but reject obviously-future timestamps. + return { valid: false, error: 'Signed payload is from the future' }; + } + + // Delegate subscription field/shape/expiry validation. + const inner = verifySubscription((env.payload as { subscription: unknown }).subscription); + if (!inner.valid) return inner; + + return { valid: true, subscription: inner.subscription }; +} diff --git a/packages/licensing/tsconfig.json b/packages/licensing/tsconfig.json index bd8a6183..51b8593c 100644 --- a/packages/licensing/tsconfig.json +++ b/packages/licensing/tsconfig.json @@ -4,7 +4,8 @@ "outDir": "./dist", "rootDir": "./src", "noEmit": false, - "declaration": true + "declaration": true, + "types": ["node"] }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "__tests__"] diff --git a/packages/licensing/vitest.config.ts b/packages/licensing/vitest.config.ts index 8e730d50..0799f5fc 100644 --- a/packages/licensing/vitest.config.ts +++ b/packages/licensing/vitest.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: true, environment: 'node', + coverage: sharedCoverage, }, }); diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 48f1620e..e2c8819b 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -17,16 +17,17 @@ "dev": "tsx src/index.ts", "test": "vitest run" }, + "engines": { + "node": ">=22.5.0" + }, "dependencies": { "@modelcontextprotocol/sdk": "^1.29.0", - "better-sqlite3": "^11.7.0", - "zod": "^4.3.6" + "zod": "^4.4.3" }, "devDependencies": { - "@types/better-sqlite3": "^7.6.0", - "@types/node": "^22.0.0", - "tsx": "^4.19.0", - "typescript": "^5.7.0", - "vitest": "^3.2.1" + "@types/node": "^25.9.2", + "tsx": "^4.22.4", + "typescript": "^6.0.3", + "vitest": "^4.1.8" } } diff --git a/packages/mcp-server/src/__tests__/fts5-triggers.test.ts b/packages/mcp-server/src/__tests__/fts5-triggers.test.ts index 5c3e4bad..8b687fca 100644 --- a/packages/mcp-server/src/__tests__/fts5-triggers.test.ts +++ b/packages/mcp-server/src/__tests__/fts5-triggers.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import Database from 'better-sqlite3'; +import { DatabaseSync } from 'node:sqlite'; import { openDb } from '../db.js'; /** @@ -70,10 +70,10 @@ const SCHEMA = ` `; describe('FTS5 trigger execution', () => { - let db: Database.Database; + let db: DatabaseSync; beforeEach(() => { - db = new Database(':memory:'); + db = new DatabaseSync(':memory:'); db.exec(SCHEMA); }); diff --git a/packages/mcp-server/src/db.ts b/packages/mcp-server/src/db.ts index 86866401..25a3440b 100644 --- a/packages/mcp-server/src/db.ts +++ b/packages/mcp-server/src/db.ts @@ -1,19 +1,19 @@ /** * Database connection for the MCP server. * - * Opens the Readied SQLite database using better-sqlite3 (native). - * The MCP server runs as a standalone Node.js process, so native - * modules work without Electron conflicts. This gives full feature - * parity with the desktop app, including FTS5 support and WAL - * concurrency for safe concurrent access to the same DB file. + * Uses node:sqlite (built into Node 22+) — no native compilation, no ABI + * conflicts with Electron's bundled Node. The MCP server runs as a standalone + * Node.js process invoked by the host (Claude Code), sharing the same DB file + * as the desktop app via WAL mode for safe concurrent access. FTS5 ships + * enabled in node:sqlite's bundled SQLite build. */ -import Database from 'better-sqlite3'; +import { DatabaseSync } from 'node:sqlite'; import { existsSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; -export type { Database as BetterSqlite3Database } from 'better-sqlite3'; +export type Database = DatabaseSync; function getDbPath(): string { if (process.env.READIED_DB_PATH) { @@ -50,12 +50,7 @@ function getDbPath(): string { ); } -/** - * Verify that the SQLite build includes FTS5. - * Uses sqlite_compileoption_used() to check without touching the schema, - * avoiding the risk of a stale temp table if the process crashes mid-check. - */ -function assertFts5Available(db: Database.Database): void { +function assertFts5Available(db: DatabaseSync): void { const row = db.prepare("SELECT sqlite_compileoption_used('ENABLE_FTS5') AS v").get() as | { v: number } | undefined; @@ -68,11 +63,11 @@ function assertFts5Available(db: Database.Database): void { } } -export function openDb(dbPath?: string): Database.Database { +export function openDb(dbPath?: string): DatabaseSync { const resolvedPath = dbPath ?? getDbPath(); - const db = new Database(resolvedPath); + const db = new DatabaseSync(resolvedPath); if (resolvedPath !== ':memory:') { - db.pragma('journal_mode = WAL'); + db.exec('PRAGMA journal_mode = WAL'); } assertFts5Available(db); return db; diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 0b88cad7..d47df4b7 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -4,7 +4,7 @@ * Readied MCP Server * * Exposes Readied notes to Claude Code via the Model Context Protocol. - * Reads directly from the local SQLite database using better-sqlite3. + * Reads directly from the local SQLite database using node:sqlite (Node 22.5+). * * Tools: * - readied_list_notes: List notes with optional filters @@ -19,30 +19,23 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; -import type Database from 'better-sqlite3'; +import type { Database } from './db.js'; import { openDb } from './db.js'; -/** Helper: run a SELECT and return rows as objects */ -function query( - db: Database.Database, - sql: string, - params: unknown[] = [] -): Record[] { - return db.prepare(sql).all(...params) as Record[]; +function query(db: Database, sql: string, params: unknown[] = []): Record[] { + return db.prepare(sql).all(...(params as never[])) as Record[]; } -/** Helper: run a single SELECT and return first row */ function queryOne( - db: Database.Database, + db: Database, sql: string, params: unknown[] = [] ): Record | null { - return (db.prepare(sql).get(...params) as Record) ?? null; + return (db.prepare(sql).get(...(params as never[])) as Record) ?? null; } -/** Helper: run INSERT/UPDATE/DELETE and return rows changed */ -function execute(db: Database.Database, sql: string, params: unknown[] = []): number { - return db.prepare(sql).run(...params).changes; +function execute(db: Database, sql: string, params: unknown[] = []): number { + return Number(db.prepare(sql).run(...(params as never[])).changes); } /** Escape and prepare a query string for FTS5 MATCH syntax */ @@ -53,7 +46,7 @@ function prepareFtsQuery(input: string): string { return terms.map(t => `"${t}"*`).join(' OR '); } -function createServer(db: Database.Database) { +function createServer(db: Database) { const server = new McpServer({ name: 'readied', version: '0.1.0', @@ -61,14 +54,16 @@ function createServer(db: Database.Database) { // ── List notes ────────────────────────────────────────────────────────── - server.tool( + server.registerTool( 'readied_list_notes', - 'List notes in Readied. Returns titles, IDs, and metadata.', { - notebook: z.string().optional().describe('Filter by notebook name'), - limit: z.number().default(20).describe('Max notes to return'), - includeTrash: z.boolean().default(false).describe('Include trashed notes'), - status: z.enum(['active', 'on_hold', 'completed', 'dropped']).optional(), + description: 'List notes in Readied. Returns titles, IDs, and metadata.', + inputSchema: { + notebook: z.string().optional().describe('Filter by notebook name'), + limit: z.number().default(20).describe('Max notes to return'), + includeTrash: z.boolean().default(false).describe('Include trashed notes'), + status: z.enum(['active', 'on_hold', 'completed', 'dropped']).optional(), + }, }, async ({ notebook, limit, includeTrash, status }) => { let sql = ` @@ -111,12 +106,14 @@ function createServer(db: Database.Database) { // ── Read note ─────────────────────────────────────────────────────────── - server.tool( + server.registerTool( 'readied_read_note', - 'Read the full content of a note by ID or title search.', { - id: z.string().optional().describe('Note ID (exact match)'), - title: z.string().optional().describe('Title to search for (partial match)'), + description: 'Read the full content of a note by ID or title search.', + inputSchema: { + id: z.string().optional().describe('Note ID (exact match)'), + title: z.string().optional().describe('Title to search for (FTS5)'), + }, }, async ({ id, title }) => { let note: Record | null = null; @@ -124,11 +121,29 @@ function createServer(db: Database.Database) { if (id) { note = queryOne(db, 'SELECT id, title, content, notebook_id FROM notes WHERE id = ?', [id]); } else if (title) { + // Use FTS5 to find the best-matching live note by title. + // Falls back to a parameterized LIKE if FTS produced no hit, so this + // tool still works on freshly-restored DBs where the FTS index is empty. + const ftsQuery = prepareFtsQuery(title); note = queryOne( db, - 'SELECT id, title, content, notebook_id FROM notes WHERE title LIKE ? AND is_deleted = 0 ORDER BY updated_at DESC LIMIT 1', - [`%${title}%`] + `SELECT n.id, n.title, n.content, n.notebook_id + FROM notes_fts + JOIN notes n ON n.id = notes_fts.id + WHERE notes_fts MATCH ? AND n.is_deleted = 0 + ORDER BY bm25(notes_fts) LIMIT 1`, + [ftsQuery] ); + if (!note) { + note = queryOne( + db, + `SELECT id, title, content, notebook_id + FROM notes + WHERE title LIKE '%' || ? || '%' AND is_deleted = 0 + ORDER BY updated_at DESC LIMIT 1`, + [title] + ); + } } if (!note) { @@ -143,12 +158,14 @@ function createServer(db: Database.Database) { // ── Create note ───────────────────────────────────────────────────────── - server.tool( + server.registerTool( 'readied_create_note', - 'Create a new note in Readied. Content should be markdown.', { - content: z.string().describe('Markdown content for the note'), - notebook: z.string().optional().describe('Notebook name (defaults to Inbox)'), + description: 'Create a new note in Readied. Content should be markdown.', + inputSchema: { + content: z.string().describe('Markdown content for the note'), + notebook: z.string().optional().describe('Notebook name (defaults to Inbox)'), + }, }, async ({ content, notebook }) => { const id = crypto.randomUUID(); @@ -181,12 +198,14 @@ function createServer(db: Database.Database) { // ── Update note ───────────────────────────────────────────────────────── - server.tool( + server.registerTool( 'readied_update_note', - 'Update an existing note. Replaces the full content.', { - id: z.string().describe('Note ID'), - content: z.string().describe('New markdown content'), + description: 'Update an existing note. Replaces the full content.', + inputSchema: { + id: z.string().describe('Note ID'), + content: z.string().describe('New markdown content'), + }, }, async ({ id, content }) => { const now = new Date().toISOString(); @@ -212,12 +231,15 @@ function createServer(db: Database.Database) { // ── Search notes (FTS5) ────────────────────────────────────────────────── - server.tool( + server.registerTool( 'readied_search_notes', - 'Full-text search across all notes using FTS5 with relevance ranking. Returns matching notes with snippets.', { - query: z.string().describe('Search query'), - limit: z.number().default(10), + description: + 'Full-text search across all notes using FTS5 with relevance ranking. Returns matching notes with snippets.', + inputSchema: { + query: z.string().describe('Search query'), + limit: z.number().default(10), + }, }, async ({ query: q, limit }) => { const trimmed = q.trim(); @@ -253,30 +275,39 @@ function createServer(db: Database.Database) { // ── List notebooks ────────────────────────────────────────────────────── - server.tool('readied_list_notebooks', 'List all notebooks in Readied.', {}, async () => { - const notebooks = query( - db, - `SELECT nb.id, nb.name, nb.parent_id, COUNT(n.id) as note_count + server.registerTool( + 'readied_list_notebooks', + { + description: 'List all notebooks in Readied.', + inputSchema: {}, + }, + async () => { + const notebooks = query( + db, + `SELECT nb.id, nb.name, nb.parent_id, COUNT(n.id) as note_count FROM notebooks nb LEFT JOIN notes n ON n.notebook_id = nb.id AND n.is_deleted = 0 GROUP BY nb.id ORDER BY nb.name` - ); + ); - const text = notebooks - .map(nb => `- **${nb.name}** (${nb.note_count} notes) — ID: ${nb.id}`) - .join('\n'); + const text = notebooks + .map(nb => `- **${nb.name}** (${nb.note_count} notes) — ID: ${nb.id}`) + .join('\n'); - return { content: [{ type: 'text' as const, text: text || 'No notebooks found.' }] }; - }); + return { content: [{ type: 'text' as const, text: text || 'No notebooks found.' }] }; + } + ); // ── Trash note ────────────────────────────────────────────────────────── - server.tool( + server.registerTool( 'readied_trash_note', - 'Move a note to trash (soft delete).', { - id: z.string().describe('Note ID'), + description: 'Move a note to trash (soft delete).', + inputSchema: { + id: z.string().describe('Note ID'), + }, }, async ({ id }) => { const changes = execute( diff --git a/packages/mcp-server/tsconfig.json b/packages/mcp-server/tsconfig.json index 756b4bcd..25c386a0 100644 --- a/packages/mcp-server/tsconfig.json +++ b/packages/mcp-server/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "noEmit": false, "outDir": "dist", - "rootDir": "src" + "rootDir": "src", + "types": ["node"] }, "include": ["src"], "exclude": ["src/__tests__"] diff --git a/packages/plugin-api/package.json b/packages/plugin-api/package.json index a33d1989..3ae57296 100644 --- a/packages/plugin-api/package.json +++ b/packages/plugin-api/package.json @@ -25,12 +25,12 @@ }, "devDependencies": { "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.41.1", - "@types/react": "^19.2.14", - "react": "^19.2.5", - "typescript": "^5.7.2", - "vitest": "^4.1.0", - "zustand": "^5.0.12" + "@codemirror/view": "^6.43.0", + "@types/react": "^19.2.17", + "react": "^19.2.7", + "typescript": "^6.0.3", + "vitest": "^4.1.8", + "zustand": "^5.0.14" }, "license": "MIT" } diff --git a/packages/plugin-api/src/editor/types.ts b/packages/plugin-api/src/editor/types.ts deleted file mode 100644 index 6ec32d25..00000000 --- a/packages/plugin-api/src/editor/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type { EditorAPI } from '../types'; diff --git a/packages/plugin-api/vitest.config.ts b/packages/plugin-api/vitest.config.ts index 8996a048..edde6c9d 100644 --- a/packages/plugin-api/vitest.config.ts +++ b/packages/plugin-api/vitest.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: true, environment: 'node', include: ['tests/**/*.test.ts'], + coverage: sharedCoverage, }, }); diff --git a/packages/plugin-cli/package.json b/packages/plugin-cli/package.json index c2889981..bac175c0 100644 --- a/packages/plugin-cli/package.json +++ b/packages/plugin-cli/package.json @@ -19,9 +19,9 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "@types/node": "^25.9.2", + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/product-config/package.json b/packages/product-config/package.json index b5d0dc1b..3ec1cda1 100644 --- a/packages/product-config/package.json +++ b/packages/product-config/package.json @@ -17,8 +17,8 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/product-config/vitest.config.ts b/packages/product-config/vitest.config.ts index 8e730d50..0799f5fc 100644 --- a/packages/product-config/vitest.config.ts +++ b/packages/product-config/vitest.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: true, environment: 'node', + coverage: sharedCoverage, }, }); diff --git a/packages/storage-core/package.json b/packages/storage-core/package.json index 9cc2a20a..c35e5c57 100644 --- a/packages/storage-core/package.json +++ b/packages/storage-core/package.json @@ -21,9 +21,9 @@ "@readied/core": "workspace:*" }, "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "@types/node": "^25.9.2", + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/storage-core/src/interfaces/index.ts b/packages/storage-core/src/interfaces/index.ts deleted file mode 100644 index 64f23885..00000000 --- a/packages/storage-core/src/interfaces/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Interface exports - */ - -export type { DatabaseAdapter, PreparedStatement, StatementResult } from './DatabaseAdapter.js'; - -export type { Migration, MigrationRecord } from './Migration.js'; - -export type { ExtendedNoteRepository } from './ExtendedNoteRepository.js'; diff --git a/packages/storage-core/src/migrations/index.ts b/packages/storage-core/src/migrations/index.ts deleted file mode 100644 index 39325bd2..00000000 --- a/packages/storage-core/src/migrations/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Migration exports - */ - -export { runMigrations, getPendingMigrations, getCurrentVersion } from './runner.js'; diff --git a/packages/storage-core/src/repositories/index.ts b/packages/storage-core/src/repositories/index.ts deleted file mode 100644 index 4558a7f8..00000000 --- a/packages/storage-core/src/repositories/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Repository exports - */ - -export { InMemoryNoteRepository } from './InMemoryNoteRepository.js'; diff --git a/packages/storage-core/src/types/index.ts b/packages/storage-core/src/types/index.ts deleted file mode 100644 index 753040ca..00000000 --- a/packages/storage-core/src/types/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Type exports - */ - -export type { ArchivedFilter } from './ArchivedFilter.js'; -export type { ListNotesOptions } from './ListNotesOptions.js'; -export type { NoteSnapshot } from './NoteSnapshot.js'; diff --git a/packages/storage-core/vitest.config.ts b/packages/storage-core/vitest.config.ts index 2dcea8c5..c2a23743 100644 --- a/packages/storage-core/vitest.config.ts +++ b/packages/storage-core/vitest.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: false, environment: 'node', + coverage: sharedCoverage, }, }); diff --git a/packages/storage-sqlite/package.json b/packages/storage-sqlite/package.json index 4e918892..5974f32e 100644 --- a/packages/storage-sqlite/package.json +++ b/packages/storage-sqlite/package.json @@ -23,13 +23,13 @@ "@readied/wikilinks": "workspace:*" }, "peerDependencies": { - "better-sqlite3": "^11.0.0" + "better-sqlite3": "^12.10.0" }, "devDependencies": { "@types/better-sqlite3": "^7.6.12", - "better-sqlite3": "^12.9.0", - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "better-sqlite3": "^12.10.0", + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/storage-sqlite/src/repositories/SQLiteNoteRepository.ts b/packages/storage-sqlite/src/repositories/SQLiteNoteRepository.ts index 32bf0aa6..c92b3353 100644 --- a/packages/storage-sqlite/src/repositories/SQLiteNoteRepository.ts +++ b/packages/storage-sqlite/src/repositories/SQLiteNoteRepository.ts @@ -4,25 +4,23 @@ * Implements the ExtendedNoteRepository interface from @readied/storage-core */ -import type { - ExtendedNoteRepository, - ListNotesOptions, - ArchivedFilter, -} from '@readied/storage-core'; -import { - type Note, - type NoteId, - type NoteStatus, - type Tag, - type Timestamp, - createNote, - createNoteId, - createNotebookId, - createTag, - DEFAULT_NOTE_STATUS, -} from '@readied/core'; +import type { ExtendedNoteRepository, ListNotesOptions } from '@readied/storage-core'; +import { type Note, type NoteId, type Tag, createNoteId, createTag } from '@readied/core'; import { extractWikilinks } from '@readied/wikilinks'; import type { DatabaseConnection } from '../database.js'; +import { + rowToNote, + prepareFtsQuery, + archivedConditionSql, + type NoteRow, + type TagRow, + type TagWithColorRow, + type BacklinkInfo, +} from './noteMapping.js'; + +// Re-export public types so external imports (e.g. desktop's handlers/types.ts) +// keep working unchanged. +export type { BacklinkInfo }; /** Sync history entry returned by getSyncHistory */ export interface SyncHistoryEntry { @@ -42,37 +40,6 @@ export interface SyncHistoryEntry { errorMessage: string | null; } -/** Row type from SQLite */ -interface NoteRow { - id: string; - notebook_id: string; - content: string; - title: string; - created_at: string; - updated_at: string; - word_count: number; - archived_at: string | null; - is_pinned: number; // SQLite stores booleans as 0/1 - is_deleted: number; - status: string; -} - -interface TagRow { - name: string; -} - -interface TagWithColorRow { - name: string; - color: string | null; -} - -/** Backlink information for UI display */ -export interface BacklinkInfo { - noteId: string; - noteTitle: string; - targetRef: string; -} - /** SQLite implementation of ExtendedNoteRepository */ export class SQLiteNoteRepository implements ExtendedNoteRepository { constructor(private readonly db: DatabaseConnection) {} @@ -90,7 +57,7 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { if (!row) return null; const tags = this.getTagsForNote(id); - return this.rowToNote(row, tags); + return rowToNote(row, tags); } /** Save a note (insert or update) */ @@ -156,7 +123,7 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { title: 'title', }[sortBy]; - const archivedCondition = this.getArchivedCondition(archived, 'n'); + const archivedCondition = archivedConditionSql(archived, 'n'); let sql: string; let params: (string | number)[]; @@ -189,7 +156,7 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { return rows.map(row => { const tags = this.getTagsForNote(createNoteId(row.id)); - return this.rowToNote(row, tags); + return rowToNote(row, tags); }); } @@ -207,7 +174,7 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { const archivedCondition = includeArchived ? '' : 'AND n.archived_at IS NULL'; // Prepare FTS5 query: escape special chars, add prefix matching - const ftsQuery = this.prepareFtsQuery(trimmedQuery); + const ftsQuery = prepareFtsQuery(trimmedQuery); const stmt = this.db.prepare(` SELECT n.id, n.notebook_id, n.content, n.title, n.created_at, n.updated_at, @@ -223,26 +190,11 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { return rows.map(row => { const tags = this.getTagsForNote(createNoteId(row.id)); - return this.rowToNote(row, tags); + return rowToNote(row, tags); }); } - /** Prepare query string for FTS5 MATCH syntax */ - private prepareFtsQuery(query: string): string { - // Escape FTS5 special characters: " * ^ - OR AND NOT ( ) - const escaped = query.replace(/["*^()]/g, ' ').trim(); - - // Split into terms and add prefix matching for partial word search - const terms = escaped.split(/\s+/).filter(t => t.length > 0); - - if (terms.length === 0) { - return '""'; // Empty search - } - - // Use OR between terms with prefix matching - // Each term becomes "term"* for prefix matching - return terms.map(t => `"${t}"*`).join(' OR '); - } + // prepareFtsQuery moved to noteMapping.ts /** Get total count of notes */ async count(includeArchived: boolean = false): Promise { @@ -283,17 +235,7 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { // Private helpers - private getArchivedCondition(filter: ArchivedFilter, tableAlias: string = ''): string { - const prefix = tableAlias ? `${tableAlias}.` : ''; - switch (filter) { - case 'active': - return `AND ${prefix}archived_at IS NULL`; - case 'archived': - return `AND ${prefix}archived_at IS NOT NULL`; - case 'all': - return ''; - } - } + // getArchivedCondition moved to noteMapping.archivedConditionSql private getTagsForNote(noteId: NoteId): Tag[] { const stmt = this.db.prepare(` @@ -478,38 +420,7 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { } } - private rowToNote(row: NoteRow, tags: Tag[]): Note { - // Reconstruct note from stored data with structural title - const note = createNote({ - id: createNoteId(row.id), - notebookId: createNotebookId(row.notebook_id), - title: row.title, // Structural title from DB - content: row.content, - createdAt: row.created_at as Timestamp, - isPinned: row.is_pinned === 1, - isDeleted: row.is_deleted === 1, - status: (row.status as NoteStatus) || DEFAULT_NOTE_STATUS, - }); - - // Return note with stored metadata - return { - ...note, - notebookId: createNotebookId(row.notebook_id), - title: row.title, // Ensure structural title is set - isPinned: row.is_pinned === 1, - isDeleted: row.is_deleted === 1, - status: (row.status as NoteStatus) || DEFAULT_NOTE_STATUS, - metadata: { - ...note.metadata, - title: row.title, - createdAt: row.created_at as Timestamp, - updatedAt: row.updated_at as Timestamp, - tags, - wordCount: row.word_count, - archivedAt: row.archived_at as Timestamp | null, - }, - }; - } + // rowToNote moved to noteMapping.ts // ═══════════════════════════════════════════════════════════════════════════ // Links (Wikilinks / Backlinks) @@ -737,7 +648,7 @@ export class SQLiteNoteRepository implements ExtendedNoteRepository { return rows.map(row => { const tags = this.getTagsForNote(createNoteId(row.id)); return { - note: this.rowToNote(row, tags), + note: rowToNote(row, tags), localVersion: row.local_version, lastSyncedAt: row.last_synced_at, }; diff --git a/packages/storage-sqlite/src/repositories/index.ts b/packages/storage-sqlite/src/repositories/index.ts deleted file mode 100644 index 61999496..00000000 --- a/packages/storage-sqlite/src/repositories/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Repository exports - */ - -export { SQLiteNoteRepository, type BacklinkInfo } from './SQLiteNoteRepository.js'; diff --git a/packages/storage-sqlite/src/repositories/noteMapping.ts b/packages/storage-sqlite/src/repositories/noteMapping.ts new file mode 100644 index 00000000..0ea9ae35 --- /dev/null +++ b/packages/storage-sqlite/src/repositories/noteMapping.ts @@ -0,0 +1,130 @@ +/** + * Row → Note mapping helpers and shared row types. + * + * Pure functions extracted from SQLiteNoteRepository so future + * sync / tag / archive sub-repositories can reuse them without + * depending on the main repo class. + */ + +import { + type Note, + type NoteStatus, + type Tag, + type Timestamp, + createNote, + createNoteId, + createNotebookId, + DEFAULT_NOTE_STATUS, +} from '@readied/core'; +import type { ArchivedFilter } from '@readied/storage-core'; + +/** Row shape returned by `SELECT * FROM notes` */ +export interface NoteRow { + id: string; + notebook_id: string; + content: string; + title: string; + created_at: string; + updated_at: string; + word_count: number; + archived_at: string | null; + is_pinned: number; // SQLite stores booleans as 0/1 + is_deleted: number; + status: string; +} + +/** Row shape for tag joins (just the tag name) */ +export interface TagRow { + name: string; +} + +/** Row shape for tags with their color metadata */ +export interface TagWithColorRow { + name: string; + color: string | null; +} + +/** Backlink information surfaced to the UI */ +export interface BacklinkInfo { + noteId: string; + noteTitle: string; + targetRef: string; +} + +/** + * Reconstruct a domain Note from a SQLite row plus its tags. + * + * The row carries the *stored* (structural) title — the markdown-derived + * "display" title lives elsewhere. We reuse `createNote` to get fresh + * metadata defaults, then overlay the persisted values so that + * createdAt / updatedAt / wordCount / archivedAt survive the roundtrip. + */ +export function rowToNote(row: NoteRow, tags: Tag[]): Note { + const note = createNote({ + id: createNoteId(row.id), + notebookId: createNotebookId(row.notebook_id), + title: row.title, + content: row.content, + createdAt: row.created_at as Timestamp, + isPinned: row.is_pinned === 1, + isDeleted: row.is_deleted === 1, + status: (row.status as NoteStatus) || DEFAULT_NOTE_STATUS, + }); + + return { + ...note, + notebookId: createNotebookId(row.notebook_id), + title: row.title, + isPinned: row.is_pinned === 1, + isDeleted: row.is_deleted === 1, + status: (row.status as NoteStatus) || DEFAULT_NOTE_STATUS, + metadata: { + ...note.metadata, + title: row.title, + createdAt: row.created_at as Timestamp, + updatedAt: row.updated_at as Timestamp, + tags, + wordCount: row.word_count, + archivedAt: row.archived_at as Timestamp | null, + }, + }; +} + +/** + * Build an FTS5 MATCH clause from a free-form user query. + * + * 1. Strip FTS5 special chars (" * ^ ( )) — we'll add our own. + * 2. Tokenize on whitespace. + * 3. Quote each token (defends against tokens that look like FTS keywords) + * and append `*` for prefix-matching. + * 4. Join with OR — any token match counts. + * + * Empty / all-whitespace input returns `""`, which FTS5 treats as "no + * results" rather than throwing. + */ +export function prepareFtsQuery(input: string): string { + const escaped = input.replace(/["*^()]/g, ' ').trim(); + const terms = escaped.split(/\s+/).filter(t => t.length > 0); + if (terms.length === 0) return '""'; + return terms.map(t => `"${t}"*`).join(' OR '); +} + +/** + * Build a SQL fragment that filters by archived state. + * + * Returns either an empty string (no filter) or a SQL chunk starting + * with `AND`. Caller is responsible for the WHERE. + * + * @param tableAlias prefix without trailing dot, e.g. `n` → emits `n.archived_at` + */ +export function archivedConditionSql(filter: ArchivedFilter, tableAlias: string = ''): string { + const prefix = tableAlias ? `${tableAlias}.` : ''; + switch (filter) { + case 'active': + return `AND ${prefix}archived_at IS NULL`; + case 'archived': + return `AND ${prefix}archived_at IS NOT NULL`; + case 'all': + return ''; + } +} diff --git a/packages/storage-sqlite/vitest.config.ts b/packages/storage-sqlite/vitest.config.ts index ec5d3a41..a907b681 100644 --- a/packages/storage-sqlite/vitest.config.ts +++ b/packages/storage-sqlite/vitest.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: false, environment: 'node', setupFiles: ['./tests/setup.ts'], + coverage: sharedCoverage, }, }); diff --git a/packages/sync-core/package.json b/packages/sync-core/package.json index b098e3fc..b98f2a7a 100644 --- a/packages/sync-core/package.json +++ b/packages/sync-core/package.json @@ -18,11 +18,10 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@readied/core": "workspace:*", - "zod": "^4.3.6" + "zod": "^4.4.3" }, "devDependencies": { - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" } } diff --git a/packages/tasks/package.json b/packages/tasks/package.json index 029c15a3..7b17e762 100644 --- a/packages/tasks/package.json +++ b/packages/tasks/package.json @@ -18,8 +18,8 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/tasks/vitest.config.ts b/packages/tasks/vitest.config.ts index 2dcea8c5..c2a23743 100644 --- a/packages/tasks/vitest.config.ts +++ b/packages/tasks/vitest.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: false, environment: 'node', + coverage: sharedCoverage, }, }); diff --git a/packages/wikilinks/package.json b/packages/wikilinks/package.json index 4fa0979a..9f975772 100644 --- a/packages/wikilinks/package.json +++ b/packages/wikilinks/package.json @@ -23,16 +23,15 @@ "@codemirror/view": "^6.0.0" }, "dependencies": { - "unified": "^11.0.0", "unist-util-visit": "^5.1.0" }, "devDependencies": { - "@codemirror/autocomplete": "^6.20.1", + "@codemirror/autocomplete": "^6.20.3", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.41.1", + "@codemirror/view": "^6.43.0", "@types/mdast": "^4.0.4", - "typescript": "^5.7.2", - "vitest": "^4.1.0" + "typescript": "^6.0.3", + "vitest": "^4.1.8" }, "license": "MIT" } diff --git a/packages/wikilinks/vitest.config.ts b/packages/wikilinks/vitest.config.ts index 8996a048..edde6c9d 100644 --- a/packages/wikilinks/vitest.config.ts +++ b/packages/wikilinks/vitest.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vitest/config'; +import { sharedCoverage } from '../../vitest.shared.js'; export default defineConfig({ test: { globals: true, environment: 'node', include: ['tests/**/*.test.ts'], + coverage: sharedCoverage, }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6bef84c5..a07a973b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,71 +9,77 @@ importers: .: devDependencies: '@commitlint/cli': - specifier: ^20.5.0 - version: 20.5.0(@types/node@25.6.0)(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)(typescript@5.9.3) + specifier: ^21.0.2 + version: 21.0.2(@types/node@25.9.2)(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)(typescript@6.0.3) '@commitlint/config-conventional': - specifier: ^20.5.0 - version: 20.5.0 + specifier: ^21.0.2 + version: 21.0.2 '@eslint/js': specifier: ^10.0.1 - version: 10.0.1(eslint@9.39.2(jiti@2.6.1)) + version: 10.0.1(eslint@10.4.1(jiti@2.7.0)) '@semantic-release/changelog': specifier: ^6.0.3 - version: 6.0.3(semantic-release@25.0.3(typescript@5.9.3)) + version: 6.0.3(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/commit-analyzer': specifier: ^13.0.1 - version: 13.0.1(semantic-release@25.0.3(typescript@5.9.3)) + version: 13.0.1(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/exec': specifier: ^7.1.0 - version: 7.1.0(semantic-release@25.0.3(typescript@5.9.3)) + version: 7.1.0(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/git': specifier: ^10.0.1 - version: 10.0.1(semantic-release@25.0.3(typescript@5.9.3)) + version: 10.0.1(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/github': - specifier: ^12.0.6 - version: 12.0.6(semantic-release@25.0.3(typescript@5.9.3)) + specifier: ^12.0.8 + version: 12.0.8(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/release-notes-generator': - specifier: ^14.1.0 - version: 14.1.0(semantic-release@25.0.3(typescript@5.9.3)) + specifier: ^14.1.1 + version: 14.1.1(semantic-release@25.0.3(typescript@6.0.3)) + '@vitest/coverage-v8': + specifier: ^4.1.8 + version: 4.1.8(vitest@4.1.8) conventional-changelog-conventionalcommits: specifier: ^9.3.1 version: 9.3.1 eslint: - specifier: ^9.39.2 - version: 9.39.2(jiti@2.6.1) + specifier: ^10.4.1 + version: 10.4.1(jiti@2.7.0) eslint-plugin-import-x: - specifier: ^4.16.1 - version: 4.16.1(@typescript-eslint/utils@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)) - husky: - specifier: ^9.1.7 - version: 9.1.7 + specifier: ^4.16.2 + version: 4.16.2(@typescript-eslint/utils@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)) + knip: + specifier: ^5.66.0 + version: 5.88.1(@types/node@25.9.2)(typescript@6.0.3) + lefthook: + specifier: ^1.13.6 + version: 1.13.6 lint-staged: - specifier: ^16.4.0 - version: 16.4.0 + specifier: ^17.0.7 + version: 17.0.7 prettier: - specifier: ^3.7.4 - version: 3.7.4 + specifier: ^3.8.3 + version: 3.8.3 semantic-release: specifier: ^25.0.3 - version: 25.0.3(typescript@5.9.3) + version: 25.0.3(typescript@6.0.3) turbo: - specifier: ^2.9.14 - version: 2.9.14 + specifier: ^2.9.16 + version: 2.9.16 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 typescript-eslint: - specifier: ^8.59.0 - version: 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + specifier: ^8.60.1 + version: 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) apps/desktop: dependencies: '@codemirror/autocomplete': - specifier: ^6.20.1 - version: 6.20.1 + specifier: ^6.20.3 + version: 6.20.3 '@codemirror/commands': specifier: ^6.10.3 version: 6.10.3 @@ -90,20 +96,20 @@ importers: specifier: ^6.6.0 version: 6.6.0 '@codemirror/view': - specifier: ^6.41.1 - version: 6.41.1 + specifier: ^6.43.0 + version: 6.43.0 '@lezer/highlight': specifier: ^1.2.3 version: 1.2.3 '@sentry/electron': - specifier: ^7.11.0 - version: 7.11.0 + specifier: ^7.13.0 + version: 7.13.0 '@tanstack/react-query': - specifier: ^5.100.1 - version: 5.100.1(react@19.2.5) + specifier: ^5.101.0 + version: 5.101.0(react@19.2.7) better-sqlite3: - specifier: ^12.9.0 - version: 12.9.0 + specifier: ^12.10.0 + version: 12.10.0 cross-fetch: specifier: ^4.1.0 version: 4.1.0(encoding@0.1.13) @@ -111,29 +117,20 @@ importers: specifier: ^9.0.0 version: 9.0.0 electron-updater: - specifier: ^6.8.3 - version: 6.8.3 - highlight.js: - specifier: ^11.11.1 - version: 11.11.1 + specifier: ^6.8.9 + version: 6.8.9 isomorphic-git: - specifier: ^1.37.5 - version: 1.37.5 + specifier: ^1.38.4 + version: 1.38.4 lucide-react: - specifier: ^1.8.0 - version: 1.8.0(react@19.2.5) + specifier: ^1.17.0 + version: 1.17.0(react@19.2.7) pino: specifier: ^10.3.1 version: 10.3.1 - pino-roll: - specifier: ^4.0.0 - version: 4.0.0 react-markdown: specifier: ^10.1.0 - version: 10.1.0(@types/react@19.2.14)(react@19.2.5) - react-resizable-panels: - specifier: ^4.10.0 - version: 4.10.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 10.1.0(@types/react@19.2.17)(react@19.2.7) rehype-highlight: specifier: ^7.0.2 version: 7.0.2 @@ -146,13 +143,16 @@ importers: turndown-plugin-gfm: specifier: ^1.0.2 version: 1.0.2 - unist-util-visit: - specifier: ^5.1.0 - version: 5.1.0 + zod: + specifier: ^4.4.3 + version: 4.4.3 zustand: - specifier: ^5.0.12 - version: 5.0.12(@types/react@19.2.14)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)) + specifier: ^5.0.14 + version: 5.0.14(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) devDependencies: + '@playwright/test': + specifier: ^1.49.1 + version: 1.60.0 '@readied/ai-core': specifier: workspace:* version: link:../../packages/ai-core @@ -192,72 +192,66 @@ importers: '@types/better-sqlite3': specifier: ^7.6.12 version: 7.6.13 - '@types/mdast': - specifier: ^4.0.4 - version: 4.0.4 '@types/react': - specifier: ^19.2.14 - version: 19.2.14 + specifier: ^19.2.17 + version: 19.2.17 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.17) '@types/turndown': specifier: ^5.0.6 version: 5.0.6 '@vitejs/plugin-react': - specifier: ^6.0.1 - version: 6.0.1(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^6.0.2 + version: 6.0.2(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) electron: - specifier: ^41.3.0 - version: 41.3.0 + specifier: ^42.3.3 + version: 42.3.3 electron-builder: - specifier: ^26.8.1 - version: 26.8.1(electron-builder-squirrel-windows@26.0.12) + specifier: ^26.15.2 + version: 26.15.2(electron-builder-squirrel-windows@26.0.12) electron-devtools-installer: specifier: ^4.0.0 version: 4.0.0 electron-vite: specifier: ^5.0.0 - version: 5.0.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) - pino-pretty: - specifier: ^13.1.3 - version: 13.1.3 + version: 5.0.0(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) react: - specifier: ^19.2.5 - version: 19.2.5 + specifier: ^19.2.7 + version: 19.2.7 react-dom: - specifier: ^19.2.5 - version: 19.2.5(react@19.2.5) + specifier: ^19.2.7 + version: 19.2.7(react@19.2.7) react-force-graph-2d: specifier: ^1.29.1 - version: 1.29.1(react@19.2.5) + version: 1.29.1(react@19.2.7) rehype-raw: specifier: ^7.0.0 version: 7.0.0 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vite: - specifier: ^8.0.10 - version: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + specifier: ^8.0.16 + version: 8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) apps/web: dependencies: '@radix-ui/react-accordion': - specifier: ^1.2.12 - version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: ^1.2.13 + version: 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-dialog': - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: ^1.1.16 + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-separator': - specifier: ^1.1.8 - version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: ^1.1.9 + version: 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': - specifier: ^1.2.4 - version: 1.2.4(@types/react@19.2.14)(react@19.2.5) + specifier: ^1.2.5 + version: 1.2.5(@types/react@19.2.17)(react@19.2.7) '@readied/product-config': specifier: workspace:* version: link:../../packages/product-config @@ -268,41 +262,41 @@ importers: specifier: ^2.1.1 version: 2.1.1 framer-motion: - specifier: ^12.38.0 - version: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: ^12.40.0 + version: 12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) fumadocs-core: - specifier: ^16.8.2 - version: 16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6) + specifier: ^16.9.3 + version: 16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) fumadocs-mdx: - specifier: ^14.3.1 - version: 14.3.1(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)(vite@8.0.10(@types/node@25.4.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^15.0.11 + version: 15.0.11(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(rolldown@1.0.3)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) fumadocs-ui: - specifier: ^16.8.2 - version: 16.8.2(@tailwindcss/oxide@4.2.4)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4) + specifier: ^16.9.3 + version: 16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0) lucide-react: - specifier: ^1.8.0 - version: 1.8.0(react@19.2.5) + specifier: ^1.17.0 + version: 1.17.0(react@19.2.7) marked: - specifier: ^18.0.2 - version: 18.0.2 + specifier: ^18.0.5 + version: 18.0.5 next: - specifier: ^16.2.6 - version: 16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: ^16.2.7 + version: 16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-themes: specifier: ^0.4.6 - version: 0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: - specifier: ^19.2.5 - version: 19.2.5 + specifier: ^19.2.7 + version: 19.2.7 react-dom: - specifier: ^19.2.5 - version: 19.2.5(react@19.2.5) + specifier: ^19.2.7 + version: 19.2.7(react@19.2.7) tailwind-merge: - specifier: ^3.5.0 - version: 3.5.0 + specifier: ^3.6.0 + version: 3.6.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@4.2.4) + version: 1.0.7(tailwindcss@4.3.0) devDependencies: '@fontsource-variable/jetbrains-mono': specifier: ^5.2.8 @@ -311,84 +305,84 @@ importers: specifier: ^5.2.8 version: 5.2.8 '@tailwindcss/postcss': - specifier: ^4.2.4 - version: 4.2.4 + specifier: ^4.3.0 + version: 4.3.0 '@types/node': - specifier: 25.4.0 - version: 25.4.0 + specifier: 25.9.2 + version: 25.9.2 '@types/react': - specifier: ^19.2.14 - version: 19.2.14 + specifier: ^19.2.17 + version: 19.2.17 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.17) postcss: - specifier: ^8.5.10 - version: 8.5.10 + specifier: ^8.5.15 + version: 8.5.15 tailwindcss: - specifier: ^4.2.4 - version: 4.2.4 + specifier: ^4.3.0 + version: 4.3.0 typescript: - specifier: ^5.7.0 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 packages/ai-core: devDependencies: typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/api: dependencies: '@hono/zod-validator': - specifier: ^0.7.6 - version: 0.7.6(hono@4.12.23)(zod@4.3.6) + specifier: ^0.8.0 + version: 0.8.0(hono@4.12.23)(zod@4.4.3) '@libsql/client': specifier: ^0.17.3 version: 0.17.3 drizzle-orm: specifier: ^0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260423.1)(@libsql/client@0.17.3)(@neondatabase/serverless@0.10.4)(@opentelemetry/api@1.9.1)(@types/better-sqlite3@7.6.13)(@types/pg@8.15.6)(better-sqlite3@12.9.0)(gel@2.2.0)(sql.js@1.14.1) + version: 0.45.2(@cloudflare/workers-types@4.20260608.1)(@libsql/client@0.17.3)(@neondatabase/serverless@0.10.4)(@opentelemetry/api@1.9.1)(@types/better-sqlite3@7.6.13)(@types/pg@8.15.6)(better-sqlite3@12.10.0)(gel@2.2.0)(sql.js@1.14.1) hono: - specifier: ^4.12.21 + specifier: ^4.12.23 version: 4.12.23 jose: - specifier: ^6.2.2 - version: 6.2.2 + specifier: ^6.2.3 + version: 6.2.3 stripe: - specifier: ^22.0.2 - version: 22.0.2(@types/node@25.6.0) + specifier: ^22.2.0 + version: 22.2.0(@types/node@25.9.2) zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@cloudflare/workers-types': - specifier: ^4.20260423.1 - version: 4.20260423.1 + specifier: ^4.20260608.1 + version: 4.20260608.1 drizzle-kit: specifier: ^0.31.10 version: 0.31.10 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) wrangler: - specifier: ^4.84.1 - version: 4.84.1(@cloudflare/workers-types@4.20260423.1) + specifier: ^4.98.0 + version: 4.98.0(@cloudflare/workers-types@4.20260608.1) packages/command-registry: devDependencies: typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/commands: devDependencies: @@ -399,27 +393,27 @@ importers: specifier: ^6.6.0 version: 6.6.0 '@codemirror/view': - specifier: ^6.41.1 - version: 6.41.1 + specifier: ^6.43.0 + version: 6.43.0 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/core: dependencies: zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/embeds: dependencies: @@ -431,17 +425,17 @@ importers: specifier: ^6.6.0 version: 6.6.0 '@codemirror/view': - specifier: ^6.41.1 - version: 6.41.1 + specifier: ^6.43.0 + version: 6.43.0 '@types/mdast': specifier: ^4.0.4 version: 4.0.4 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/licensing: dependencies: @@ -453,42 +447,36 @@ importers: version: link:../product-config devDependencies: '@types/node': - specifier: ^22.10.2 - version: 22.19.3 + specifier: ^25.9.2 + version: 25.9.2 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.19.3)(vite@8.0.10(@types/node@22.19.3)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/mcp-server: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.29.0 - version: 1.29.0(zod@4.3.6) - better-sqlite3: - specifier: ^11.7.0 - version: 11.10.0 + version: 1.29.0(zod@4.4.3) zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: - '@types/better-sqlite3': - specifier: ^7.6.0 - version: 7.6.13 '@types/node': - specifier: ^22.0.0 - version: 22.19.3 + specifier: ^25.9.2 + version: 25.9.2 tsx: - specifier: ^4.19.0 - version: 4.21.0 + specifier: ^4.22.4 + version: 4.22.4 typescript: - specifier: ^5.7.0 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^3.2.1 - version: 3.2.6(@types/debug@4.1.13)(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/plugin-api: devDependencies: @@ -496,44 +484,44 @@ importers: specifier: ^6.6.0 version: 6.6.0 '@codemirror/view': - specifier: ^6.41.1 - version: 6.41.1 + specifier: ^6.43.0 + version: 6.43.0 '@types/react': - specifier: ^19.2.14 - version: 19.2.14 + specifier: ^19.2.17 + version: 19.2.17 react: - specifier: ^19.2.5 - version: 19.2.5 + specifier: ^19.2.7 + version: 19.2.7 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) zustand: - specifier: ^5.0.12 - version: 5.0.12(@types/react@19.2.14)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)) + specifier: ^5.0.14 + version: 5.0.14(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) packages/plugin-cli: devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.27 + specifier: ^25.9.2 + version: 25.9.2 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@20.19.27)(vite@8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/product-config: devDependencies: typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/storage-core: dependencies: @@ -542,14 +530,14 @@ importers: version: link:../core devDependencies: '@types/node': - specifier: ^20.0.0 - version: 20.19.27 + specifier: ^25.9.2 + version: 25.9.2 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@20.19.27)(vite@8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/storage-sqlite: dependencies: @@ -567,67 +555,61 @@ importers: specifier: ^7.6.12 version: 7.6.13 better-sqlite3: - specifier: ^12.9.0 - version: 12.9.0 + specifier: ^12.10.0 + version: 12.10.0 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/sync-core: dependencies: - '@readied/core': - specifier: workspace:* - version: link:../core zod: - specifier: ^4.3.6 - version: 4.3.6 + specifier: ^4.4.3 + version: 4.4.3 devDependencies: typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/tasks: devDependencies: typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages/wikilinks: dependencies: - unified: - specifier: ^11.0.0 - version: 11.0.5 unist-util-visit: specifier: ^5.1.0 version: 5.1.0 devDependencies: '@codemirror/autocomplete': - specifier: ^6.20.1 - version: 6.20.1 + specifier: ^6.20.3 + version: 6.20.3 '@codemirror/state': specifier: ^6.6.0 version: 6.6.0 '@codemirror/view': - specifier: ^6.41.1 - version: 6.41.1 + specifier: ^6.43.0 + version: 6.43.0 '@types/mdast': specifier: ^4.0.4 version: 4.0.4 typescript: - specifier: ^5.7.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.8 + version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) packages: @@ -752,10 +734,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -769,6 +759,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-transform-arrow-functions@7.27.1': resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} engines: {node: '>=6.9.0'} @@ -787,54 +782,62 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@cloudflare/kv-asset-handler@0.4.2': - resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} - engines: {node: '>=18.0.0'} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@cloudflare/kv-asset-handler@0.5.0': + resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} + engines: {node: '>=22.0.0'} - '@cloudflare/unenv-preset@2.16.0': - resolution: {integrity: sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==} + '@cloudflare/unenv-preset@2.16.1': + resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} peerDependencies: unenv: 2.0.0-rc.24 - workerd: 1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0 + workerd: '>1.20260305.0 <2.0.0-0' peerDependenciesMeta: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260421.1': - resolution: {integrity: sha512-DLU5ZTZ1VHeZZnj0PuVJEMHKGisfLe2XShyImP5P/PPj/m/t7CLEJmPiI7FMxvT7ynArkckJl7m+Z5x7u4Kkdw==} + '@cloudflare/workerd-darwin-64@1.20260603.1': + resolution: {integrity: sha512-cEXDWu6V3ZrpmwWkM4OJE9AeXjdAgOY5rh8EHhcBVCuP5rxnzUbPzLtrVOHx0UUUAcCrFq0Xsa6mZKL1VUZsKQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260421.1': - resolution: {integrity: sha512-Trotq3xRAkIcpC505WoxM8+kIH4JIvOJCNuRatyHcz9uF5S+ukgiVUFUlM+GIjw1uCM/Bda2St+vSniX1RZdpw==} + '@cloudflare/workerd-darwin-arm64@1.20260603.1': + resolution: {integrity: sha512-uBPK4LaWJNbbCYwPnUAehlHbbVulhVZPZsdcAhBPfZhHb3QAuAEPAQepO/P67R3V6Cni4YGx1fLbL8A5wwoaNA==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260421.1': - resolution: {integrity: sha512-938QjUv0z+QqK6BAvgwX/lCIZ2b224ZXoXtGTbhyNVMhB+mt4Dj24cj9qca4ekNXjVM7uTKp1yOHZO97fVSacw==} + '@cloudflare/workerd-linux-64@1.20260603.1': + resolution: {integrity: sha512-ht9l6/8Tk7Rp6kA4S9oFZ4X8u0VjnnFdmU/6B3fnABYKREYTKh2RdOqXqXxcp5eNJseireKnWik/hQOPK1CutQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260421.1': - resolution: {integrity: sha512-YI4+mLfwnJcKJ+iPyxzx+tp2Jy4o29BxBPSQGZxl/AZyvZ9eTKsmNZmtjEiT4i3O/M0tdO/B/d9ESDHbRCs2rQ==} + '@cloudflare/workerd-linux-arm64@1.20260603.1': + resolution: {integrity: sha512-LJZ6x00rAjSrobV4m0ZW0TpH5ilBbKcWBzlH+y+KOUsIE/CpTuhAzKV43TbSnFLRX5+jrWKiz2v0hO91lPXy6A==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260421.1': - resolution: {integrity: sha512-q1SFgwlNH9lFmw74vh7EJbJtduo92Nx51mNOfd3/u6pux6AldcwRviYzKEEv3FEbtv6OBB7J8D5f8vtZj7Z6Sg==} + '@cloudflare/workerd-windows-64@1.20260603.1': + resolution: {integrity: sha512-DvwqkXMAJRPoDN4PxapAwhlz/6ouD+6R1ttbAEK3cWD/QBvFF5STx7Ds/9Irf+rBly3np3uHWkeX+wZnNFEuzA==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20260423.1': - resolution: {integrity: sha512-SHIc0NeJMtn0sW043eWtMxYFbJ9VPSLkcx+FEqCk0uZLD3HrWT+5xWhm6EYiOYDg0vnrlXNHcu2ly/01zDh3bw==} + '@cloudflare/workers-types@4.20260608.1': + resolution: {integrity: sha512-Yz90KXPZBB9K/AhsnLaA321s5+i5ZpWZRFbcGx9dWjyknQJXPAS1pK5CJSFEedwY6zoEr1cf2SkpBATL1idsWA==} - '@codemirror/autocomplete@6.20.1': - resolution: {integrity: sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==} + '@codemirror/autocomplete@6.20.3': + resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} '@codemirror/commands@6.10.3': resolution: {integrity: sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==} @@ -917,81 +920,81 @@ packages: '@codemirror/state@6.6.0': resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==} - '@codemirror/view@6.41.1': - resolution: {integrity: sha512-ToDnWKbBnke+ZLrP6vgTTDScGi5H37YYuZGniQaBzxMVdtCxMrslsmtnOvbPZk4RX9bvkQqnWR/WS/35tJA0qg==} + '@codemirror/view@6.43.0': + resolution: {integrity: sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==} '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@commitlint/cli@20.5.0': - resolution: {integrity: sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==} - engines: {node: '>=v18'} + '@commitlint/cli@21.0.2': + resolution: {integrity: sha512-YMmfLbqBg+ZRvvmPhc+cilSQFrh/AgzVgCT1U/OifmUZEwPbvCtA8rN//YNaF9d5eoZphxVMGYtmwA2QgQORgg==} + engines: {node: '>=22.12.0'} hasBin: true - '@commitlint/config-conventional@20.5.0': - resolution: {integrity: sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==} - engines: {node: '>=v18'} + '@commitlint/config-conventional@21.0.2': + resolution: {integrity: sha512-P/ZRhryQmkj0Z0dY9FOoRwe3xkwJyyAdtXwt01NT2kuZttcG2CNYp1q5Ci3u+nDT2jcbJRw2kt13Czl1qKNPfg==} + engines: {node: '>=22.12.0'} - '@commitlint/config-validator@20.5.0': - resolution: {integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==} - engines: {node: '>=v18'} + '@commitlint/config-validator@21.0.1': + resolution: {integrity: sha512-Zd2UFdndeMMaW2O96HK0tdfT4gOImUvidMpAd/pws2zZ4m1nrAZ/9b/v2JYuE8fs86GpXv9F7LNaIuCIWhY+pA==} + engines: {node: '>=22.12.0'} - '@commitlint/ensure@20.5.0': - resolution: {integrity: sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==} - engines: {node: '>=v18'} + '@commitlint/ensure@21.0.1': + resolution: {integrity: sha512-jJ1037967wU7YN/xkv+iRlOBlmaOXPhPO5KQSqya6GyXzBlwuLzELBFao16DVg9dZyqmNrhewzwZ3SAibetHBQ==} + engines: {node: '>=22.12.0'} - '@commitlint/execute-rule@20.0.0': - resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} - engines: {node: '>=v18'} + '@commitlint/execute-rule@21.0.1': + resolution: {integrity: sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==} + engines: {node: '>=22.12.0'} - '@commitlint/format@20.5.0': - resolution: {integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==} - engines: {node: '>=v18'} + '@commitlint/format@21.0.1': + resolution: {integrity: sha512-ksmG2+cHGtuDPQQbhBbC4unwm444+6TiPw0d1bKf67hntgZqZ8E0g1MuYKUuyT5IH4IMmXZhKq22/Z3jBvtQIw==} + engines: {node: '>=22.12.0'} - '@commitlint/is-ignored@20.5.0': - resolution: {integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==} - engines: {node: '>=v18'} + '@commitlint/is-ignored@21.0.2': + resolution: {integrity: sha512-H5z4t8PC9tUsmZ/o+EptM3Nq8sTFtskAShdcqxCoyzklW5eaVT5xbrDAET2uypzir9Vsj4ZZmBtyKjYe2XqgeQ==} + engines: {node: '>=22.12.0'} - '@commitlint/lint@20.5.0': - resolution: {integrity: sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==} - engines: {node: '>=v18'} + '@commitlint/lint@21.0.2': + resolution: {integrity: sha512-PnUmLYGeGLfW8oVatR9KpNxSHYAnJOEWlMZzfdeFOUq6WUrFx1fGQaWCWJqMoIll/xPM+GdfJV+tKHZVHhl0Fg==} + engines: {node: '>=22.12.0'} - '@commitlint/load@20.5.0': - resolution: {integrity: sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==} - engines: {node: '>=v18'} + '@commitlint/load@21.0.2': + resolution: {integrity: sha512-lwUE70hN0/qE/ZRROhbaX65ly/FF12DrqfReLCESo37M0OQCFAf2jRS+2tSCSORq+bm4Kdju7qNDj46uc1QzTA==} + engines: {node: '>=22.12.0'} - '@commitlint/message@20.4.3': - resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==} - engines: {node: '>=v18'} + '@commitlint/message@21.0.2': + resolution: {integrity: sha512-5n4aqHGD/FNnom/D5L8i7cYtV+xjuXcBL832C3w9VglEsZzIsoHpJsvxzJ7cgiOsOdc/2jU4t5+7qMHh7GBX3g==} + engines: {node: '>=22.12.0'} - '@commitlint/parse@20.5.0': - resolution: {integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==} - engines: {node: '>=v18'} + '@commitlint/parse@21.0.2': + resolution: {integrity: sha512-QVZJhGHTm+oiuWyEKOCTQ0ZM3mfJ0eGWFeHuj7WzSKEth+UukcCHac9GD8pgdFlg/qGkFWOtyaNd1T8REgagaw==} + engines: {node: '>=22.12.0'} - '@commitlint/read@20.5.0': - resolution: {integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==} - engines: {node: '>=v18'} + '@commitlint/read@21.0.2': + resolution: {integrity: sha512-BtsrnLVycSSKf4Q0gMch4giCj5NNlmcbhc8ra5vONgGtP2IjRDo33bEFtr5Pm+2N+5fXGWb2MksWPrspPfdhdw==} + engines: {node: '>=22.12.0'} - '@commitlint/resolve-extends@20.5.0': - resolution: {integrity: sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==} - engines: {node: '>=v18'} + '@commitlint/resolve-extends@21.0.1': + resolution: {integrity: sha512-0DhjYWL6uYrY16Efa032fYk3woGJDU4AGWiG1XXltT9AMUNYKyb5cIZU2ivbaMZ3+kKFqUjikD2cjh66Sbh/Sg==} + engines: {node: '>=22.12.0'} - '@commitlint/rules@20.5.0': - resolution: {integrity: sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==} - engines: {node: '>=v18'} + '@commitlint/rules@21.0.2': + resolution: {integrity: sha512-k6tQ69Td7t2qUSIbik8D3TL1q3ZJpkEbV+yLogDzCRAdOxJm4ndhtBNREsLA1/puRfWvzS9eioF2w43WT+hHgQ==} + engines: {node: '>=22.12.0'} - '@commitlint/to-lines@20.0.0': - resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} - engines: {node: '>=v18'} + '@commitlint/to-lines@21.0.1': + resolution: {integrity: sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==} + engines: {node: '>=22.12.0'} - '@commitlint/top-level@20.4.3': - resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==} - engines: {node: '>=v18'} + '@commitlint/top-level@21.0.2': + resolution: {integrity: sha512-s9KKM+e+mXgFeIh4n7KmOGAVT3mkJ3Fp1bBYHIK5pjeUwlEMzp/tZfb5u0Poa680AsQTXMEMRxZi1vQ9m2X5ug==} + engines: {node: '>=22.12.0'} - '@commitlint/types@20.5.0': - resolution: {integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==} - engines: {node: '>=v18'} + '@commitlint/types@21.0.1': + resolution: {integrity: sha512-4u7w8jcoCUFWhjWnASYzZHAP34OqOtuFBN87nQmFvqda03YU0T6z+yB4w0gSAMpekiRqqGk5rt+qSlW+a2vSEg==} + engines: {node: '>=22.12.0'} '@conventional-changelog/git-client@2.7.0': resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==} @@ -1030,16 +1033,16 @@ packages: resolution: {integrity: sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==} hasBin: true - '@electron/get@2.0.3': - resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} - engines: {node: '>=12'} - '@electron/get@3.1.0': resolution: {integrity: sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==} engines: {node: '>=14'} - '@electron/node-gyp@git+https://git@github.com:electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2': - resolution: {commit: 06b29aafb7708acef8b3669835c8a7857ebc92d2, repo: git@github.com:electron/node-gyp.git, type: git} + '@electron/get@5.0.0': + resolution: {integrity: sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==} + engines: {node: '>=22.12.0'} + + '@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2': + resolution: {tarball: https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2} version: 10.2.0-electron.1 engines: {node: '>=12.13.0'} hasBin: true @@ -1084,12 +1087,21 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/core@1.11.0': + resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.0': + resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==} + '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} deprecated: 'Merged into tsx: https://tsx.is' @@ -1708,21 +1720,17 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.6.0': + resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/eslintrc@3.3.3': - resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/js@10.0.1': resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} @@ -1733,37 +1741,33 @@ packages: eslint: optional: true - '@eslint/js@9.39.2': - resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.7.2': + resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@fastify/otel@0.18.0': resolution: {integrity: sha512-3TASCATfw+ctICSb4ymrv7iCm0qJ0N9CarB+CZ7zIJ7KqNbwI5JjyDL1/sxoC0ccTO1Zyd1iQ+oqncPg5FJXaA==} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@floating-ui/core@1.7.4': - resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} - '@floating-ui/dom@1.7.5': - resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==} + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} - '@floating-ui/react-dom@2.1.7': - resolution: {integrity: sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==} + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/utils@0.2.10': - resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} '@fontsource-variable/jetbrains-mono@5.2.8': resolution: {integrity: sha512-WBA9elru6Jdp5df2mES55wuOO0WIrn3kpXnI4+W2ek5u3ZgLS9XS4gmIlcQhiZOWEKl95meYdvK7xI+ETLCq/Q==} @@ -1791,18 +1795,22 @@ packages: peerDependencies: hono: ^4 - '@hono/zod-validator@0.7.6': - resolution: {integrity: sha512-Io1B6d011Gj1KknV4rXYz4le5+5EubcWEU/speUjuw9XMMIaP3n78yXLhjd2A3PXaXaUwEAluOiAyLqhBEJgsw==} + '@hono/zod-validator@0.8.0': + resolution: {integrity: sha512-5uS4S1/LKtZQYvD4BtpPUFkOv8d1wNxHHrChm26buMiEYc1FrHWvDUaKVBwkiVtvSExHSpLGDvcnpI2Copyj9w==} peerDependencies: - hono: '>=3.9.0' + hono: '>=4.10.0' zod: ^3.25.0 || ^4.0.0 - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -1950,14 +1958,6 @@ packages: cpu: [x64] os: [win32] - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2138,53 +2138,53 @@ packages: '@neondatabase/serverless@0.10.4': resolution: {integrity: sha512-2nZuh3VUO9voBauuh+IGYRhGU/MskWHt1IuZvHcJw6GLjDgtqj/KViKo7SIrLdGLdot7vFbiRRw+BgEy3wT9HA==} - '@next/env@16.2.6': - resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==} + '@next/env@16.2.7': + resolution: {integrity: sha512-tMJizPlj6ZYpBMMdK8S0LJufrP4QTdR6pcv9KQ/bVETPAmg0j1mlHE9G2c38UyGHxoBapgwuj7XjbGJ2RcDFOg==} - '@next/swc-darwin-arm64@16.2.6': - resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==} + '@next/swc-darwin-arm64@16.2.7': + resolution: {integrity: sha512-vm1EDI/pVaBNNiychmxk3fft+OhQPVD9cIM/tReLZIQ3TfQ4kqI9DwKk00dzuS1ulC7icbrzCFrmRRlk9PfNdw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.6': - resolution: {integrity: sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==} + '@next/swc-darwin-x64@16.2.7': + resolution: {integrity: sha512-O3IRSv1ZBL1zs0WrIgefTEcTKFVn+ryxBNe54erJ6KsD+2f/Mmt7g2jOYh8PSBdUwPtKQJuCsTMlZ7tIu2AcsQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.6': - resolution: {integrity: sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==} + '@next/swc-linux-arm64-gnu@16.2.7': + resolution: {integrity: sha512-Re6PZtjBDd0aMU+VcZcC/PrIvj4WhrjDYtMhhCVQamWN4L90EVP0pcEOBQD25prSlw7OzNw5QpHLWMilRLsRNw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@16.2.6': - resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==} + '@next/swc-linux-arm64-musl@16.2.7': + resolution: {integrity: sha512-qyogG9QtBzWxgJfeGBvOEHI3851gTfCF3wLZ5RDLTBJGAmE9p1qDwKCOdrBrvBzRvYDT+gUDp72pzlSEfAXgNA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@16.2.6': - resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==} + '@next/swc-linux-x64-gnu@16.2.7': + resolution: {integrity: sha512-Vhe4ZDuBpmMogrGi5D4R2Kq4JAQlj6+wvgaFYy31zfES0zPmt6TLA+cuYpM/OLrPZjo2MYQTHVqNUSCR6+fDZQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@16.2.6': - resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==} + '@next/swc-linux-x64-musl@16.2.7': + resolution: {integrity: sha512-srvian89JahFLw1YLBEuhvPJ0DO5lpUeJQMXy4xYo7g628ZlNgXdNkqoxSAv9OYrBfByh6vxISMwW/mRbzCY+g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@16.2.6': - resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==} + '@next/swc-win32-arm64-msvc@16.2.7': + resolution: {integrity: sha512-GX3wvLpULFuRFJzwHaKfm7QZJ18F4ZSuxlPJ96BoBglCzBmdSjyeBKF+ZhWhvL/ckxNfLnNa7bsObO2ipYpszw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.6': - resolution: {integrity: sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==} + '@next/swc-win32-x64-msvc@16.2.7': + resolution: {integrity: sha512-J4WlM72NMk076Qsg0jTdK3SNXatlSdnjW7L7oNGLst1tAGjHrJh/FYi+pw9wyIjEtGRKDNzD0zuiY16oWYWVaw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2192,6 +2192,26 @@ packages: '@noble/ed25519@3.1.0': resolution: {integrity: sha512-pfcObRY3CtvwfaG9Mt5XqZdKmAQppl37tHUeuBhDUbiwJBCVY4/A4lbMvb1xKhMDx96AqAqZpMWuBX1HulhX4g==} + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@2.2.0': + resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} + engines: {node: '>= 20.19.0'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@npmcli/fs@2.1.2': resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -2265,20 +2285,14 @@ packages: resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-async-hooks@2.7.0': - resolution: {integrity: sha512-MWXggArM+Y11mPS8VOrqxOj+YMGQSRuvhM91eSBX4xFpJa05mpkeVvM8pPux5ElkEjV5RMgrkisrlP/R83SpBQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.6.1': resolution: {integrity: sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.7.0': - resolution: {integrity: sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==} + '@opentelemetry/core@2.7.1': + resolution: {integrity: sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -2301,12 +2315,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-express@0.62.0': - resolution: {integrity: sha512-Tvx+vgAZKEQxU3Rx+xWLiR0mLxHwmk69/8ya04+VsV9WYh8w6Lhx5hm5yAMvo1wy0KqWgFKBLwSeo3sHCwdOww==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-fs@0.33.0': resolution: {integrity: sha512-sCZWXGalQ01wr3tAhSR9ucqFJ0phidpAle6/17HVjD6gN8FLmZMK/8sKxdXYHy3PbnlV1P4zeiSVFNKpbFMNLA==} engines: {node: ^18.19.0 || >=20.6.0} @@ -2409,12 +2417,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-undici@0.24.0': - resolution: {integrity: sha512-oKzZ3uvqP17sV0EsoQcJgjEfIp0kiZRbYu/eD8p13Cbahumf8lb/xpYeNr/hfAJ4owzEtIDcGIjprfLcYbIKBQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.7.0 - '@opentelemetry/instrumentation@0.207.0': resolution: {integrity: sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==} engines: {node: ^18.19.0 || >=20.6.0} @@ -2437,14 +2439,14 @@ packages: resolution: {integrity: sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw==} engines: {node: ^18.19.0 || >=20.6.0} - '@opentelemetry/resources@2.7.0': - resolution: {integrity: sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==} + '@opentelemetry/resources@2.7.1': + resolution: {integrity: sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.7.0': - resolution: {integrity: sha512-Yg9zEXJB50DLVLpsKPk7NmNqlPlS+OvqhJGh0A8oawIOTPOwlm4eXs9BMJV7L79lvEwI+dWtAj+YjTyddV336A==} + '@opentelemetry/sdk-trace-base@2.7.1': + resolution: {integrity: sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' @@ -2463,8 +2465,120 @@ packages: resolution: {integrity: sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==} engines: {node: '>= 20.0.0'} - '@oxc-project/types@0.127.0': - resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + + '@oxc-resolver/binding-android-arm-eabi@11.20.0': + resolution: {integrity: sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.20.0': + resolution: {integrity: sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.20.0': + resolution: {integrity: sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.20.0': + resolution: {integrity: sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.20.0': + resolution: {integrity: sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0': + resolution: {integrity: sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0': + resolution: {integrity: sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.20.0': + resolution: {integrity: sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-musl@11.20.0': + resolution: {integrity: sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0': + resolution: {integrity: sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==} + cpu: [ppc64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0': + resolution: {integrity: sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-musl@11.20.0': + resolution: {integrity: sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-s390x-gnu@11.20.0': + resolution: {integrity: sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==} + cpu: [s390x] + os: [linux] + + '@oxc-resolver/binding-linux-x64-gnu@11.20.0': + resolution: {integrity: sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-linux-x64-musl@11.20.0': + resolution: {integrity: sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-openharmony-arm64@11.20.0': + resolution: {integrity: sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.20.0': + resolution: {integrity: sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.20.0': + resolution: {integrity: sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.20.0': + resolution: {integrity: sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==} + cpu: [x64] + os: [win32] + + '@package-json/types@0.0.12': + resolution: {integrity: sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw==} + + '@peculiar/asn1-schema@2.7.0': + resolution: {integrity: sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg==} + + '@peculiar/json-schema@1.1.12': + resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} + engines: {node: '>=8.0.0'} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/webcrypto@1.7.1': + resolution: {integrity: sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==} + engines: {node: '>=14.18.0'} '@petamoriken/float16@3.9.3': resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==} @@ -2476,6 +2590,11 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@playwright/test@1.60.0': + resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} + engines: {node: '>=18'} + hasBin: true + '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -2502,14 +2621,14 @@ packages: peerDependencies: '@opentelemetry/api': ^1.8 - '@radix-ui/number@1.1.1': - resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + '@radix-ui/number@1.1.2': + resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} - '@radix-ui/react-accordion@1.2.12': - resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + '@radix-ui/react-accordion@1.2.13': + resolution: {integrity: sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2521,8 +2640,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.7': - resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + '@radix-ui/react-arrow@1.1.9': + resolution: {integrity: sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2534,8 +2653,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.12': - resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + '@radix-ui/react-collapsible@1.1.13': + resolution: {integrity: sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2547,8 +2666,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + '@radix-ui/react-collection@1.1.9': + resolution: {integrity: sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2560,8 +2679,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2569,8 +2688,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2578,8 +2697,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + '@radix-ui/react-dialog@1.1.16': + resolution: {integrity: sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2591,8 +2710,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.1': - resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2600,8 +2719,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + '@radix-ui/react-dismissable-layer@1.1.12': + resolution: {integrity: sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2613,8 +2732,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-guards@1.1.3': - resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2622,8 +2741,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-scope@1.1.7': - resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + '@radix-ui/react-focus-scope@1.1.9': + resolution: {integrity: sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2635,30 +2754,17 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-navigation-menu@1.2.14': - resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: '@types/react': '*' - '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-popover@1.1.15': - resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + '@radix-ui/react-navigation-menu@1.2.15': + resolution: {integrity: sha512-/fS8hKCcRt4DwCGa5QIB3juRXmfYSOk4a2AEe/BDIyy7Hm+eje2Y13oUx5zejl+wFt1owrM7E8NWlbaEl5EGpg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2670,8 +2776,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.8': - resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + '@radix-ui/react-popover@1.1.16': + resolution: {integrity: sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2683,8 +2789,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + '@radix-ui/react-popper@1.3.0': + resolution: {integrity: sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2696,8 +2802,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + '@radix-ui/react-portal@1.1.11': + resolution: {integrity: sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2709,8 +2815,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2722,8 +2828,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.4': - resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + '@radix-ui/react-primitive@2.1.5': + resolution: {integrity: sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2735,8 +2841,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.11': - resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + '@radix-ui/react-roving-focus@1.1.12': + resolution: {integrity: sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2748,8 +2854,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-scroll-area@1.2.10': - resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + '@radix-ui/react-scroll-area@1.2.11': + resolution: {integrity: sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2761,8 +2867,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-separator@1.1.8': - resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==} + '@radix-ui/react-separator@1.1.9': + resolution: {integrity: sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2774,17 +2880,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.4': - resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + '@radix-ui/react-slot@1.2.5': + resolution: {integrity: sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2792,8 +2889,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-tabs@1.1.13': - resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + '@radix-ui/react-tabs@1.1.14': + resolution: {integrity: sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2805,8 +2902,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2814,8 +2911,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2823,8 +2920,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2832,8 +2929,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + '@radix-ui/react-use-escape-keydown@1.1.2': + resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2841,8 +2938,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2850,8 +2947,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-previous@1.1.1': - resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + '@radix-ui/react-use-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2859,8 +2956,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-rect@1.1.1': - resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2868,8 +2965,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -2877,8 +2974,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-visually-hidden@1.2.3': - resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + '@radix-ui/react-visually-hidden@1.2.5': + resolution: {integrity: sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2890,251 +2987,123 @@ packages: '@types/react-dom': optional: true - '@radix-ui/rect@1.1.1': - resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} - '@rolldown/binding-android-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.17': - resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': - resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': - resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': - resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.17': - resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@rolldown/pluginutils@1.0.0-rc.7': - resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@rollup/rollup-android-arm-eabi@4.61.0': - resolution: {integrity: sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==} - cpu: [arm] - os: [android] + '@semantic-release/changelog@6.0.3': + resolution: {integrity: sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==} + engines: {node: '>=14.17'} + peerDependencies: + semantic-release: '>=18.0.0' - '@rollup/rollup-android-arm64@4.61.0': - resolution: {integrity: sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==} - cpu: [arm64] - os: [android] + '@semantic-release/commit-analyzer@13.0.1': + resolution: {integrity: sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==} + engines: {node: '>=20.8.1'} + peerDependencies: + semantic-release: '>=20.1.0' - '@rollup/rollup-darwin-arm64@4.61.0': - resolution: {integrity: sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==} - cpu: [arm64] - os: [darwin] + '@semantic-release/error@3.0.0': + resolution: {integrity: sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==} + engines: {node: '>=14.17'} - '@rollup/rollup-darwin-x64@4.61.0': - resolution: {integrity: sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.61.0': - resolution: {integrity: sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.61.0': - resolution: {integrity: sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.61.0': - resolution: {integrity: sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.61.0': - resolution: {integrity: sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.61.0': - resolution: {integrity: sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.61.0': - resolution: {integrity: sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loong64-gnu@4.61.0': - resolution: {integrity: sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-loong64-musl@4.61.0': - resolution: {integrity: sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.61.0': - resolution: {integrity: sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-ppc64-musl@4.61.0': - resolution: {integrity: sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.61.0': - resolution: {integrity: sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.61.0': - resolution: {integrity: sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.61.0': - resolution: {integrity: sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.61.0': - resolution: {integrity: sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.61.0': - resolution: {integrity: sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-openbsd-x64@4.61.0': - resolution: {integrity: sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.61.0': - resolution: {integrity: sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.61.0': - resolution: {integrity: sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.61.0': - resolution: {integrity: sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.61.0': - resolution: {integrity: sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.61.0': - resolution: {integrity: sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==} - cpu: [x64] - os: [win32] - - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - - '@semantic-release/changelog@6.0.3': - resolution: {integrity: sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==} - engines: {node: '>=14.17'} - peerDependencies: - semantic-release: '>=18.0.0' - - '@semantic-release/commit-analyzer@13.0.1': - resolution: {integrity: sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==} - engines: {node: '>=20.8.1'} - peerDependencies: - semantic-release: '>=20.1.0' - - '@semantic-release/error@3.0.0': - resolution: {integrity: sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==} - engines: {node: '>=14.17'} - - '@semantic-release/error@4.0.0': - resolution: {integrity: sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==} - engines: {node: '>=18'} + '@semantic-release/error@4.0.0': + resolution: {integrity: sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==} + engines: {node: '>=18'} '@semantic-release/exec@7.1.0': resolution: {integrity: sha512-4ycZ2atgEUutspPZ2hxO6z8JoQt4+y/kkHvfZ1cZxgl9WKJId1xPj+UadwInj+gMn2Gsv+fLnbrZ4s+6tK2TFQ==} @@ -3148,8 +3117,8 @@ packages: peerDependencies: semantic-release: '>=18.0.0' - '@semantic-release/github@12.0.6': - resolution: {integrity: sha512-aYYFkwHW3c6YtHwQF0t0+lAjlU+87NFOZuH2CvWFD0Ylivc7MwhZMiHOJ0FMpIgPpCVib/VUAcOwvrW0KnxQtA==} + '@semantic-release/github@12.0.8': + resolution: {integrity: sha512-tej5AAgK5X9wHRoDmYhecMXEHEkFeGOY1XsEblKxu8pIQwahzf1STYyr7iPU6Lpbg6C5I3N2w/ocXrBo+L7jhw==} engines: {node: ^22.14.0 || >= 24.10.0} peerDependencies: semantic-release: '>=24.1.0' @@ -3160,114 +3129,107 @@ packages: peerDependencies: semantic-release: '>=20.1.0' - '@semantic-release/release-notes-generator@14.1.0': - resolution: {integrity: sha512-CcyDRk7xq+ON/20YNR+1I/jP7BYKICr1uKd1HHpROSnnTdGqOTburi4jcRiTYz0cpfhxSloQO3cGhnoot7IEkA==} + '@semantic-release/release-notes-generator@14.1.1': + resolution: {integrity: sha512-Pbd2e2XRMUD0OxehHpgd5/YghsE76cddkRHSoDvKLK+OCy4Ewxn49rWR631MEUU01lgwF/uyVXvbnVuu6+Z6VA==} engines: {node: '>=20.8.1'} peerDependencies: semantic-release: '>=20.1.0' - '@sentry-internal/browser-utils@10.47.0': - resolution: {integrity: sha512-bVFRAeJWMBcBCvJKIFCMJ1/yQToL4vPGqfmlnDZeypcxkqUDKQ/Y3ziLHXoDL2sx0lagcgU2vH1QhCQ67Aujjw==} + '@sentry-internal/browser-utils@10.50.0': + resolution: {integrity: sha512-42bxyRTxnCmYlWnvz4CxikuQNanw8UNma2WJrtxJ0f1MAJV2GhQGSHDLnA+lvFlmiz6qct3pfen/NXGyOTegTA==} engines: {node: '>=18'} - '@sentry-internal/feedback@10.47.0': - resolution: {integrity: sha512-pdvMmi4dQpX5S/vAAzrhHPIw3T3HjUgDNgUiCBrlp7N9/6zGO2gNPhUnNekP+CjgI/z0rvf49RLqlDenpNrMOg==} + '@sentry-internal/feedback@10.50.0': + resolution: {integrity: sha512-0k9XZF0wn86f77mIO2U3gNNyDZooy139CnEanRzHinrN106vVzvBZ6TUEQoHtoO1fqQxr+nWWVrqV/PXUqk47w==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@10.47.0': - resolution: {integrity: sha512-A5OY8friSe6g8WAK4L8IeOPiEd9D3Ps40DzRH5j2f6SUja0t90mKMvHRcRf8zq0d4BkdB+JM7tjOkwxpuv8heA==} + '@sentry-internal/replay-canvas@10.50.0': + resolution: {integrity: sha512-jx6RKBmcJSWdI92qDGS/sBv1w+7Cww879Z/moX7bw7ipHa/Ts3iDcB3rgZwvhmi17U+mvYsbJeL2DXkPo3TjPw==} engines: {node: '>=18'} - '@sentry-internal/replay@10.47.0': - resolution: {integrity: sha512-ScdovxP7hJxgMt70+7hFvwT02GIaIUAxdEM/YPsayZBeCoAukPW8WiwztJfoKtsfPyKJ5A6f0H3PIxTPcA9Row==} + '@sentry-internal/replay@10.50.0': + resolution: {integrity: sha512-51FYNfnvVLAWw1rrEWPFfwHuMRb9mkVCFGA4J9/un7SpeGBsQDziGB0Di4fsCxI7+EdSBpfLHPF0csKtCCw0oQ==} engines: {node: '>=18'} - '@sentry/browser@10.47.0': - resolution: {integrity: sha512-rC0agZdxKA5XWfL4VwPOr/rJMogXDqZgnVzr93YWpFn9DMZT/7LzxSJVPIJwRUjx3bFEby3PcTa3YaX7pxm1AA==} + '@sentry/browser@10.50.0': + resolution: {integrity: sha512-1f6rAvET6myiTaSeYqvaaBwvq1LfxqWjAPIoAW/NVC9bPMkeEcuvgDajHrnZMrBeWoJ81NMyoLkyX+iOc7MoFA==} engines: {node: '>=18'} - '@sentry/core@10.47.0': - resolution: {integrity: sha512-nsYRAx3EWezDut+Zl+UwwP07thh9uY7CfSAi2whTdcJl5hu1nSp2z8bba7Vq/MGbNLnazkd3A+GITBEML924JA==} + '@sentry/core@10.50.0': + resolution: {integrity: sha512-J4A+vzUO3adl0TkFCjaN1+4miamrjHiEIYuLHiuu1lmAjq5WIVw32ObvAh4yMwNtxyaEMosTrrh5M6f12XSJFg==} engines: {node: '>=18'} - '@sentry/electron@7.11.0': - resolution: {integrity: sha512-AKz66R/o/tULOg23zJyQZU2RK2uyV7PRYEWxDeyGDIfJeg+tXN1Zwjf/WuPcpoVE3xsXcCGBReboqMLgff587Q==} + '@sentry/electron@7.13.0': + resolution: {integrity: sha512-zW/1c9fKafCZsvhRRp9mIDH9bSvzBiIUwoN087zDDHc0vatVsqqai8nUk0bUewwYZY4Inia7r5w+kPWi8LXZzg==} peerDependencies: - '@sentry/node-native': 10.47.0 + '@sentry/node-native': 10.50.0 peerDependenciesMeta: '@sentry/node-native': optional: true - '@sentry/node-core@10.47.0': - resolution: {integrity: sha512-qv6LsqHbkQmd0aQEUox/svRSz26J+l4gGjFOUNEay2armZu9XLD+Ct89jpFgZD5oIPNAj2jraodTRqydXiwS5w==} + '@sentry/node-core@10.50.0': + resolution: {integrity: sha512-Eb1BYf4Lc7ZYmdX3acKP6SgyGikrBA370gbGHaWI5jRu7G7vig8sIu1ghPmY5AlvqBPOetado7GniXr6fAXbTw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/exporter-trace-otlp-http': '>=0.57.0 <1' '@opentelemetry/instrumentation': '>=0.57.1 <1' - '@opentelemetry/resources': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 '@opentelemetry/semantic-conventions': ^1.39.0 peerDependenciesMeta: '@opentelemetry/api': optional: true - '@opentelemetry/context-async-hooks': - optional: true '@opentelemetry/core': optional: true '@opentelemetry/exporter-trace-otlp-http': optional: true '@opentelemetry/instrumentation': optional: true - '@opentelemetry/resources': - optional: true '@opentelemetry/sdk-trace-base': optional: true '@opentelemetry/semantic-conventions': optional: true - '@sentry/node@10.47.0': - resolution: {integrity: sha512-R+btqPepv88o635G6HtVewLjqCLUedBg5HBs7Nq1qbbKvyti01uArUF2f+3DsLenk5B9LUNiRlE+frZA44Ahmw==} + '@sentry/node@10.50.0': + resolution: {integrity: sha512-TvwzFQu8MGKzMQ2/tqxcNzFA8UG2kKTB+GDmA4uOzx3+GT849YZRRSJzEXCmYhk1teVd2fbmgqyYY2nyLF5a+Q==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.47.0': - resolution: {integrity: sha512-f6Hw2lrpCjlOksiosP0Z2jK/+l+21SIdoNglVeG/sttMyx8C8ywONKh0Ha50sFsvB1VaB8n94RKzzf3hkh9V3g==} + '@sentry/opentelemetry@10.50.0': + resolution: {integrity: sha512-axn3pgDPveGdaMUC0abMCmFN7ux2pA5ebPufCef4lMIsyg7BBQvaEJ+vE19wjstMaBCAJGsdZlL3eeP2rtgRMw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 '@opentelemetry/semantic-conventions': ^1.39.0 - '@shikijs/core@4.0.2': - resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} + '@shikijs/core@4.2.0': + resolution: {integrity: sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==} engines: {node: '>=20'} - '@shikijs/engine-javascript@4.0.2': - resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==} + '@shikijs/engine-javascript@4.2.0': + resolution: {integrity: sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==} engines: {node: '>=20'} - '@shikijs/engine-oniguruma@4.0.2': - resolution: {integrity: sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==} + '@shikijs/engine-oniguruma@4.2.0': + resolution: {integrity: sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g==} engines: {node: '>=20'} - '@shikijs/langs@4.0.2': - resolution: {integrity: sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==} + '@shikijs/langs@4.2.0': + resolution: {integrity: sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==} engines: {node: '>=20'} - '@shikijs/primitive@4.0.2': - resolution: {integrity: sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==} + '@shikijs/primitive@4.2.0': + resolution: {integrity: sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==} engines: {node: '>=20'} - '@shikijs/themes@4.0.2': - resolution: {integrity: sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==} + '@shikijs/themes@4.2.0': + resolution: {integrity: sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==} engines: {node: '>=20'} - '@shikijs/types@4.0.2': - resolution: {integrity: sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==} + '@shikijs/types@4.2.0': + resolution: {integrity: sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==} engines: {node: '>=20'} '@shikijs/vscode-textmate@10.0.2': @@ -3306,65 +3268,65 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} - '@tailwindcss/node@4.2.4': - resolution: {integrity: sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==} + '@tailwindcss/node@4.3.0': + resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} - '@tailwindcss/oxide-android-arm64@4.2.4': - resolution: {integrity: sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==} + '@tailwindcss/oxide-android-arm64@4.3.0': + resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.2.4': - resolution: {integrity: sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==} + '@tailwindcss/oxide-darwin-arm64@4.3.0': + resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.4': - resolution: {integrity: sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==} + '@tailwindcss/oxide-darwin-x64@4.3.0': + resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.4': - resolution: {integrity: sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==} + '@tailwindcss/oxide-freebsd-x64@4.3.0': + resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4': - resolution: {integrity: sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.4': - resolution: {integrity: sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==} + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.2.4': - resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==} + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.2.4': - resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==} + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.2.4': - resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==} + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.2.4': - resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==} + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -3375,30 +3337,30 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.4': - resolution: {integrity: sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==} + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.4': - resolution: {integrity: sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==} + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.2.4': - resolution: {integrity: sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==} + '@tailwindcss/oxide@4.3.0': + resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==} engines: {node: '>= 20'} - '@tailwindcss/postcss@4.2.4': - resolution: {integrity: sha512-wgAVj6nUWAolAu8YFvzT2cTBIElWHkjZwFYovF+xsqKsW2ADxM/X2opxj5NsF/qVccAOjRNe8X2IdPzMsWyHTg==} + '@tailwindcss/postcss@4.3.0': + resolution: {integrity: sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==} - '@tanstack/query-core@5.100.1': - resolution: {integrity: sha512-awvQhOO/2TrSCHE5LKKsXcvvj6WSBncwEcMFCB/ez0Qs0b17iyyivoGArNV3HFfXryZwCpnb/olsaBBKrIbtSw==} + '@tanstack/query-core@5.101.0': + resolution: {integrity: sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==} - '@tanstack/react-query@5.100.1': - resolution: {integrity: sha512-UgWRLhQKprC37SsO6y1zRabOqDmM2gsdTNPbqTT35yl7kOOhwXU4nyfOiGHXPwoEFJV1IpSk85hjIFjNFWVpzw==} + '@tanstack/react-query@5.101.0': + resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==} peerDependencies: react: ^18 || ^19 @@ -3406,41 +3368,41 @@ packages: resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} engines: {node: '>= 10'} - '@turbo/darwin-64@2.9.14': - resolution: {integrity: sha512-t7QiPflaEyBE4oayeZtSmu4mEfjgIrcNlNNl1z1dmIVPqEdtA7+CfTf8d7KXsOGPh6aNgWjKxyvQg9uGfDQF+A==} + '@turbo/darwin-64@2.9.16': + resolution: {integrity: sha512-jLjApWTSNd7JZ5JaLYfelW1ytnGQOvB7ivl+2RD1xQvJTbi8I9gBjzcga7tDZVPyaxpl10YTfJt3BrYXR18KDw==} cpu: [x64] os: [darwin] - '@turbo/darwin-arm64@2.9.14': - resolution: {integrity: sha512-d23147mC9BsCPA9mJ0h/ubcpbRgcJBXbcG3+Vq7YLhjz3IXuvQsJ1UXH8f4MD76ZjJ4m/E4aRdJV+MW88CDfbw==} + '@turbo/darwin-arm64@2.9.16': + resolution: {integrity: sha512-YPgrn+5HIGzrx0O2a631SV4MBQUe4W/DafMFUuBVgaU32PW9/OTT0ehviF0QSxTXuRJlHvW2eUTemddF5/spmw==} cpu: [arm64] os: [darwin] - '@turbo/linux-64@2.9.14': - resolution: {integrity: sha512-P3ZKB5tuUDdDQWuAsACGUR1qv9W7BNWxdxqVJ0kZNuNNPRaVYTPPikLcp79+GiEcW3npsR+KyP38lnQiBc5aSA==} + '@turbo/linux-64@2.9.16': + resolution: {integrity: sha512-vAEf1H6l26lTpl9FJ/peQo1NUB8RC0sbEJJz5mPcUhHA2bPDup2x3CZPgo/bH8S4cUcBLm4FN3UHd5iUO2RAew==} cpu: [x64] os: [linux] - '@turbo/linux-arm64@2.9.14': - resolution: {integrity: sha512-ZRTlzcUMrrPv9ZuDzRF9n60Ym13bKeG9jDB8WjxyLhWNzV+AJQN+zdpIk3NJYf2zQsGUm1mNar2P0elRzLw25g==} + '@turbo/linux-arm64@2.9.16': + resolution: {integrity: sha512-xDBLR2PZg4BrQOchfG6svgpv5FCNJ2TOtT2psLdEJcdKo1BH+pnPs9Xj6pvUjgfkHbuvBOfeE4R6tvxMoQKDHQ==} cpu: [arm64] os: [linux] - '@turbo/windows-64@2.9.14': - resolution: {integrity: sha512-exanwN6sIduZwykYeiTQj8kCmOhazP5WOz3bvXMcYtjhL6Z3iRWLewKrXCBq0bqwSP3iBMb/AerRCnHI4lx46A==} + '@turbo/windows-64@2.9.16': + resolution: {integrity: sha512-NBAJnaUiGdgkSzQwUIdOvkCkcpTSu58G/sBGa0mvBtzfvFOOgrQwepKOOQ8cp6sWM6OcKDNFj2p1dsZA1OWjPg==} cpu: [x64] os: [win32] - '@turbo/windows-arm64@2.9.14': - resolution: {integrity: sha512-fVdCsnmYoKICsycbWuuGp6Jvi51/3G/UluFWuAUCvR8PIW5IJkAk5BM9UF8PSm0Q2IphWHFZjYEgjHsh3B9y/g==} + '@turbo/windows-arm64@2.9.16': + resolution: {integrity: sha512-Y7SJppD0Z8wjO3Ec0ZGd9KQ4Yv0BMnA8CIowj5Vp+OEVsosXDG2weK6/t1RRLfJmc2Ozrnd6y4DOgQys+mn3WQ==} cpu: [arm64] os: [win32] '@tweenjs/tween.js@25.0.0': resolution: {integrity: sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==} - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} '@types/better-sqlite3@7.6.13': resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} @@ -3460,12 +3422,12 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -3487,8 +3449,8 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/mdx@2.0.13': - resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + '@types/mdx@2.0.14': + resolution: {integrity: sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==} '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -3496,20 +3458,14 @@ packages: '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} - '@types/node@20.19.27': - resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} - '@types/node@22.19.3': resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} - '@types/node@24.12.2': - resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} - - '@types/node@25.4.0': - resolution: {integrity: sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==} + '@types/node@24.13.1': + resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==} - '@types/node@25.6.0': - resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + '@types/node@25.9.2': + resolution: {integrity: sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3523,16 +3479,13 @@ packages: '@types/pg@8.15.6': resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} - '@types/plist@3.0.5': - resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} - '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -3549,76 +3502,73 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/verror@1.10.11': - resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.59.0': - resolution: {integrity: sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==} + '@typescript-eslint/eslint-plugin@8.60.1': + resolution: {integrity: sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.59.0 + '@typescript-eslint/parser': ^8.60.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.59.0': - resolution: {integrity: sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==} + '@typescript-eslint/parser@8.60.1': + resolution: {integrity: sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.0': - resolution: {integrity: sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==} + '@typescript-eslint/project-service@8.60.1': + resolution: {integrity: sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.59.0': - resolution: {integrity: sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==} + '@typescript-eslint/scope-manager@8.60.1': + resolution: {integrity: sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.59.0': - resolution: {integrity: sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==} + '@typescript-eslint/tsconfig-utils@8.60.1': + resolution: {integrity: sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.59.0': - resolution: {integrity: sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==} + '@typescript-eslint/type-utils@8.60.1': + resolution: {integrity: sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.51.0': - resolution: {integrity: sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==} + '@typescript-eslint/types@8.59.2': + resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.0': - resolution: {integrity: sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==} + '@typescript-eslint/types@8.60.1': + resolution: {integrity: sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.59.0': - resolution: {integrity: sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==} + '@typescript-eslint/typescript-estree@8.60.1': + resolution: {integrity: sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.0': - resolution: {integrity: sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==} + '@typescript-eslint/utils@8.60.1': + resolution: {integrity: sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.59.0': - resolution: {integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==} + '@typescript-eslint/visitor-keys@8.60.1': + resolution: {integrity: sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -3720,8 +3670,8 @@ packages: cpu: [x64] os: [win32] - '@vitejs/plugin-react@6.0.1': - resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} + '@vitejs/plugin-react@6.0.2': + resolution: {integrity: sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 @@ -3733,63 +3683,43 @@ packages: babel-plugin-react-compiler: optional: true - '@vitest/expect@3.2.6': - resolution: {integrity: sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ==} - - '@vitest/expect@4.1.0': - resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} - - '@vitest/mocker@3.2.6': - resolution: {integrity: sha512-EZOrpDbkKotFAP7wPAQV1UIyoGOk4oX7ynWhBhLB7v+meMHbQhU16oPpIYGTTe4oFlhpryGpgpcZP/sin3hYuw==} + '@vitest/coverage-v8@4.1.8': + resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==} peerDependencies: - msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + '@vitest/browser': 4.1.8 + vitest: 4.1.8 peerDependenciesMeta: - msw: - optional: true - vite: + '@vitest/browser': optional: true - '@vitest/mocker@4.1.0': - resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + '@vitest/expect@4.1.8': + resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} + + '@vitest/mocker@4.1.8': + resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.6': - resolution: {integrity: sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA==} - - '@vitest/pretty-format@4.1.0': - resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} - - '@vitest/runner@3.2.6': - resolution: {integrity: sha512-HYcoSj1w5tcgUnzoF0HcyaAQjpA1gj9ftUJ7iSJSuipc02jW9gKkigwZbjFldAfYHA1fa8UZVRftdMY5msWM9Q==} + '@vitest/pretty-format@4.1.8': + resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} - '@vitest/runner@4.1.0': - resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + '@vitest/runner@4.1.8': + resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} - '@vitest/snapshot@3.2.6': - resolution: {integrity: sha512-H+ZjNTWGpObenh0YnlBctAPnJSI20P81PL8BPzWpx54YXLLTm8hEsWawtcYLMrwvpK48hGxLLbCS+1KRXhsKhw==} + '@vitest/snapshot@4.1.8': + resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} - '@vitest/snapshot@4.1.0': - resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + '@vitest/spy@4.1.8': + resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} - '@vitest/spy@3.2.6': - resolution: {integrity: sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg==} - - '@vitest/spy@4.1.0': - resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} - - '@vitest/utils@3.2.6': - resolution: {integrity: sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg==} - - '@vitest/utils@4.1.0': - resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + '@vitest/utils@4.1.8': + resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} '@xmldom/xmldom@0.8.13': resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} @@ -3824,11 +3754,6 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} @@ -3842,6 +3767,10 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-base@9.0.0: + resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} + engines: {node: '>= 20'} + agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -3867,15 +3796,15 @@ packages: peerDependencies: ajv: ^6.9.1 - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + algoliasearch@5.46.2: resolution: {integrity: sha512-qqAXW9QvKf2tTyhpDA4qXv1IfBwD2eduSW6tUEBFIfCeE9gn9HQ9I5+MaKoenRuHrzk5sQoNh1/iof8mY7uD6Q==} engines: {node: '>= 14.0.0'} @@ -3917,12 +3846,12 @@ packages: dmg-builder: 26.0.12 electron-builder-squirrel-windows: 26.0.12 - app-builder-lib@26.8.1: - resolution: {integrity: sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw==} + app-builder-lib@26.15.2: + resolution: {integrity: sha512-3mYfKOjr/ZY7gFESOcq8kylBMgGPpmlQYnpBVit4p6zIg0t/8bkWBILdMMtnjFyN2jllyBf225T8dLlz3D6oBQ==} engines: {node: '>=14.0.0'} peerDependencies: - dmg-builder: 26.8.1 - electron-builder-squirrel-windows: 26.8.1 + dmg-builder: 26.15.2 + electron-builder-squirrel-windows: 26.15.2 argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -3937,17 +3866,16 @@ packages: array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} - assert-plus@1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} + ast-v8-to-istanbul@1.0.3: + resolution: {integrity: sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==} astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} @@ -3978,6 +3906,9 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + aws4@1.13.2: + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -3991,20 +3922,17 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.29: - resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + baseline-browser-mapping@2.10.34: + resolution: {integrity: sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==} engines: {node: '>=6.0.0'} hasBin: true before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - better-sqlite3@11.10.0: - resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==} - - better-sqlite3@12.9.0: - resolution: {integrity: sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==} - engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x} + better-sqlite3@12.10.0: + resolution: {integrity: sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==} + engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x} bezier-js@6.1.4: resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==} @@ -4018,6 +3946,9 @@ packages: blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} @@ -4029,17 +3960,14 @@ packages: bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - brace-expansion@2.1.0: - resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} - - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -4067,20 +3995,25 @@ packages: resolution: {integrity: sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==} engines: {node: '>=12.0.0'} - builder-util-runtime@9.5.1: - resolution: {integrity: sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==} + builder-util-runtime@9.7.0: + resolution: {integrity: sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==} engines: {node: '>=12.0.0'} builder-util@26.0.11: resolution: {integrity: sha512-xNjXfsldUEe153h1DraD0XvDOpqGR0L5eKFkdReB7eFW5HqysDZFfly4rckda6y9dF39N3pkPlOblcfHKGw+uA==} - builder-util@26.8.1: - resolution: {integrity: sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw==} + builder-util@26.15.0: + resolution: {integrity: sha512-dUx+HxVbiNsNQ4mGe1PyoC/tBmsHwBNDLdBuqWCj+rhHFE9lHgrXiGYKAM1uNlznhAaUSyMlms84VeSSr3gOBA==} + engines: {node: '>=14.0.0'} bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -4113,8 +4046,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001792: - resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + caniuse-lite@1.0.30001797: + resolution: {integrity: sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==} canvas-color-tracker@1.3.2: resolution: {integrity: sha512-ryQkDX26yJ3CXzb3hxUVNlg1NKE4REc5crLBq661Nxzr8TNd236SaEf2ffYLXyI5tSABSeguHLqcVq4vf9L3Zg==} @@ -4123,10 +4056,6 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} - engines: {node: '>=18'} - chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -4159,10 +4088,6 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} - chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} @@ -4231,10 +4156,6 @@ packages: resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} - cli-truncate@2.1.0: - resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} - engines: {node: '>=8'} - cli-truncate@5.2.0: resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} engines: {node: '>=20'} @@ -4280,9 +4201,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -4290,10 +4208,6 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} - engines: {node: '>=20'} - commander@5.1.0: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} engines: {node: '>= 6'} @@ -4302,8 +4216,8 @@ packages: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} - comment-parser@1.4.1: - resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + comment-parser@1.4.6: + resolution: {integrity: sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==} engines: {node: '>= 12.0.0'} compare-func@2.0.0: @@ -4407,14 +4321,20 @@ packages: typescript: optional: true + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} hasBin: true - crc@3.8.0: - resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} - crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -4511,12 +4431,6 @@ packages: resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} engines: {node: '>=12'} - date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - - dateformat@4.6.3: - resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -4529,14 +4443,13 @@ packages: decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -4602,14 +4515,8 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - dmg-builder@26.8.1: - resolution: {integrity: sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==} - - dmg-license@1.0.11: - resolution: {integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==} - engines: {node: '>=8'} - os: [darwin] - hasBin: true + dmg-builder@26.15.2: + resolution: {integrity: sha512-fMkjRqKyPtsz4Kzu/qGP0BGjqzMCIgp+/7kw/u6YH6lvn/8hvL3c0TXhoFayBoYdpPCnEinnCHztd4bW7/jetA==} dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} @@ -4740,8 +4647,8 @@ packages: electron-builder-squirrel-windows@26.0.12: resolution: {integrity: sha512-kpwXM7c/ayRUbYVErQbsZ0nQZX4aLHQrPEG9C4h9vuJCXylwFH8a7Jgi2VpKIObzCXO7LKHiCw4KdioFLFOgqA==} - electron-builder@26.8.1: - resolution: {integrity: sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw==} + electron-builder@26.15.2: + resolution: {integrity: sha512-veKM9+dCljaC5A74Pwc0ZWQ9arOHREXWh9hUIf8NGg49ch7x+IB4QhbMzIrV5ONZIXM2OEkaxW11cAPjPtoi4A==} engines: {node: '>=14.0.0'} hasBin: true @@ -4751,14 +4658,14 @@ packages: electron-publish@26.0.11: resolution: {integrity: sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==} - electron-publish@26.8.1: - resolution: {integrity: sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==} + electron-publish@26.15.1: + resolution: {integrity: sha512-BMgMHOyexWn0UnOC+Afffw0DMrr0yfLp4U8YsLXwoJ3Da7LS7WUnz21teYZqO0gaApE1KgsjREWmbPqvF5JcPg==} electron-to-chromium@1.5.344: resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==} - electron-updater@6.8.3: - resolution: {integrity: sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ==} + electron-updater@6.8.9: + resolution: {integrity: sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig==} electron-vite@5.0.0: resolution: {integrity: sha512-OHp/vjdlubNlhNkPkL/+3JD34ii5ov7M0GpuXEVdQeqdQ3ulvVR7Dg/rNBLfS5XPIFwgoBLDf9sjjrL+CuDyRQ==} @@ -4775,9 +4682,9 @@ packages: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} engines: {node: '>=8.0.0'} - electron@41.3.0: - resolution: {integrity: sha512-2Q5aeocmFdeheZGDUTrAvSR3t+n0c3d104AJWWEnt7syJU0tE4VdibMYaPtQ47QuXSoUf0/xSsfUUvu/uSXIfg==} - engines: {node: '>= 12.20.55'} + electron@42.3.3: + resolution: {integrity: sha512-0MwYp9wTb7TrtTalOYqeW+suqd9T/Znstr/nDLKqFGIjHdBZX339guo3mQqTPURRZ/UQmYM4uMpzKpI5wLptfQ==} + engines: {node: '>= 22.12.0'} hasBin: true emoji-regex@10.6.0: @@ -4802,8 +4709,8 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.20.1: - resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + enhanced-resolve@5.21.2: + resolution: {integrity: sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==} engines: {node: '>=10.13.0'} entities@6.0.1: @@ -4843,20 +4750,20 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + es-toolkit@1.47.0: + resolution: {integrity: sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw==} + es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -4914,12 +4821,12 @@ packages: unrs-resolver: optional: true - eslint-plugin-import-x@4.16.1: - resolution: {integrity: sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==} + eslint-plugin-import-x@4.16.2: + resolution: {integrity: sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/utils': ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/utils': ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint-import-resolver-node: '*' peerDependenciesMeta: '@typescript-eslint/utils': @@ -4927,25 +4834,21 @@ packages: eslint-import-resolver-node: optional: true - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-visitor-keys@5.0.1: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@9.39.2: - resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@10.4.1: + resolution: {integrity: sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: jiti: '*' @@ -4953,9 +4856,9 @@ packages: jiti: optional: true - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} @@ -5061,31 +4964,34 @@ packages: engines: {node: '>= 10.17.0'} hasBin: true - extsprintf@1.4.1: - resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} - engines: {'0': node >=0.6.0} - fast-content-type-parse@3.0.0: resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} - fast-copy@4.0.2: - resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fd-package-json@2.0.0: + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -5144,8 +5050,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} float-tooltip@1.7.5: resolution: {integrity: sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg==} @@ -5167,6 +5073,11 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + formatly@0.3.0: + resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} + engines: {node: '>=18.3.0'} + hasBin: true + forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} @@ -5174,8 +5085,8 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} - framer-motion@12.38.0: - resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} + framer-motion@12.40.0: + resolution: {integrity: sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -5192,9 +5103,6 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} - from2@2.3.0: - resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} - fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -5229,13 +5137,18 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - fumadocs-core@16.8.2: - resolution: {integrity: sha512-2HkaGmNFoTGCTYaBrNPOuJfu5o2ylw3+DQKbODvlLbw5xokqcMyfUh1A3lIJY4kouMt32wwr7aYlb+vnmx38HA==} + fumadocs-core@16.9.3: + resolution: {integrity: sha512-8RVzKnzBJR5o+tJCccY28ntekfMQYBoYiz7alnYb/d9YJc+XpnsINzTl63lQ1eBMZ9gdhm2MqRtgUjh/8rUrbw==} peerDependencies: '@mdx-js/mdx': '*' '@mixedbread/sdk': 0.x.x @@ -5253,7 +5166,7 @@ packages: react: ^19.2.0 react-dom: ^19.2.0 react-router: 7.x.x - waku: ^0.26.0 || ^0.27.0 || ^1.0.0 + waku: '*' zod: 4.x.x peerDependenciesMeta: '@mdx-js/mdx': @@ -5293,18 +5206,19 @@ packages: zod: optional: true - fumadocs-mdx@14.3.1: - resolution: {integrity: sha512-0u2eXvYrZtrJB14y6fDhP0hhxLgmH8JOmRv6IVHALt5MqR9JIJxV5LJYlho8g8CJXRE8w12rVNFZN0rtUVAqGw==} + fumadocs-mdx@15.0.11: + resolution: {integrity: sha512-XDym6obv+VVqA+MUDpaqgmTuTarrwsvo+5F5erMZQQcSqki9W7CFvqlleKOYBsUdOuXh9B3ZW3QFirdTwNpAeQ==} hasBin: true peerDependencies: '@types/mdast': '*' '@types/mdx': '*' '@types/react': '*' - fumadocs-core: ^15.0.0 || ^16.0.0 + fumadocs-core: ^16.7.0 mdast-util-directive: '*' next: ^15.3.0 || ^16.0.0 react: ^19.2.0 - vite: 6.x.x || 7.x.x || 8.x.x + rolldown: '*' + vite: 7.x.x || 8.x.x peerDependenciesMeta: '@types/mdast': optional: true @@ -5318,16 +5232,18 @@ packages: optional: true react: optional: true + rolldown: + optional: true vite: optional: true - fumadocs-ui@16.8.2: - resolution: {integrity: sha512-6NMBxt8xnkZcU9bx0ETU17ZLJWRusXSirowSHB7M7Xm5xOyGry+49q4VKDvr3uxLNl+V9doljLv9AOsGiHVpPA==} + fumadocs-ui@16.9.3: + resolution: {integrity: sha512-eoVKj1H+ATut0su+WIoPWBLRqzPMGD0hekIBr4GopWvUg1lS997HL4kP+Leyf+3CYlZtFgyXb6ylbvRLFtEj6Q==} peerDependencies: '@takumi-rs/image-response': '*' '@types/mdx': '*' '@types/react': '*' - fumadocs-core: 16.8.2 + fumadocs-core: 16.9.3 next: 16.x.x react: ^19.2.0 react-dom: ^19.2.0 @@ -5361,12 +5277,8 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.4.0: - resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} - engines: {node: '>=18'} - - get-east-asian-width@1.5.0: - resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -5389,10 +5301,6 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-stream@7.0.1: - resolution: {integrity: sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==} - engines: {node: '>=16'} - get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -5421,6 +5329,10 @@ packages: github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -5443,13 +5355,9 @@ packages: resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} engines: {node: '>=10.0'} - global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} + global-directory@5.0.0: + resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} + engines: {node: '>=20'} globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} @@ -5469,8 +5377,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} hasBin: true @@ -5493,8 +5401,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.3: - resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} hast-util-from-parse5@8.0.3: @@ -5530,9 +5438,6 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -5560,6 +5465,9 @@ packages: resolution: {integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==} engines: {node: ^20.17.0 || >=22.9.0} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} @@ -5581,6 +5489,10 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + http-proxy-agent@9.0.0: + resolution: {integrity: sha512-FcF8VhXYLQcxWCnt/cCpT2apKsRDUGeVEeMqGu4HSTu29U8Yw0TLOjdYIlDsYk3IkUh+taX4IDWpPcCqKDhCjA==} + engines: {node: '>= 20'} + http2-wrapper@1.0.3: resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} engines: {node: '>=10.19.0'} @@ -5593,6 +5505,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + https-proxy-agent@9.0.0: + resolution: {integrity: sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==} + engines: {node: '>= 20'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -5608,16 +5524,6 @@ packages: humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} - engines: {node: '>=18'} - hasBin: true - - iconv-corefoundation@1.1.7: - resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} - engines: {node: ^8.11.2 || >=10} - os: [darwin] - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -5691,9 +5597,9 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -5702,10 +5608,6 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - into-stream@7.0.0: - resolution: {integrity: sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==} - engines: {node: '>=12'} - ip-address@10.1.0: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} @@ -5828,15 +5730,27 @@ packages: resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} engines: {node: '>=20'} - isomorphic-git@1.37.5: - resolution: {integrity: sha512-wek54c5uFvd3WsxewLWt6h0GXKWQh0P8rRXns9bN1rHNjcgCb3+0lmyAsP594NeTtQFeCJQVS9b0kjbkD1l5qg==} + isomorphic-git@1.38.4: + resolution: {integrity: sha512-Ud5vs6Ac+ET+iOZWZB1j2RruVeGQSQc7U7QUhPq6iGqzifaqOVHCgRpG/8c0LwIP39R+Mr+lzR4escmCuhjONQ==} engines: {node: '>=14.17'} hasBin: true - issue-parser@7.0.1: - resolution: {integrity: sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==} + issue-parser@7.0.2: + resolution: {integrity: sha512-7atWPjhGEIX3JEtMrOYd8TKzboYlq+5sNbdl9POiLYOI14G5HZiQbZP0Xj5EZdrufQVXfJlpTV0hys0CuxwxZw==} engines: {node: ^18.17 || >=20.6.1} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -5857,26 +5771,22 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.2.2: - resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - js-yaml@4.2.0: resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true @@ -5910,8 +5820,8 @@ packages: json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json-with-bigint@3.5.7: - resolution: {integrity: sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==} + json-with-bigint@3.5.8: + resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} @@ -5941,16 +5851,77 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + knip@5.88.1: + resolution: {integrity: sha512-tpy5o7zu1MjawVkLPuahymVJekYY3kYjvzcoInhIchgePxTlo+api90tBv2KfhAIe5uXh+mez1tAfmbv8/TiZg==} + engines: {node: '>=18.18.0'} + hasBin: true + peerDependencies: + '@types/node': '>=18' + typescript: '>=5.0.4 <7' + lazy-val@1.0.5: resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} + lefthook-darwin-arm64@1.13.6: + resolution: {integrity: sha512-m6Lb77VGc84/Qo21Lhq576pEvcgFCnvloEiP02HbAHcIXD0RTLy9u2yAInrixqZeaz13HYtdDaI7OBYAAdVt8A==} + cpu: [arm64] + os: [darwin] + + lefthook-darwin-x64@1.13.6: + resolution: {integrity: sha512-CoRpdzanu9RK3oXR1vbEJA5LN7iB+c7hP+sONeQJzoOXuq4PNKVtEaN84Gl1BrVtCNLHWFAvCQaZPPiiXSy8qg==} + cpu: [x64] + os: [darwin] + + lefthook-freebsd-arm64@1.13.6: + resolution: {integrity: sha512-X4A7yfvAJ68CoHTqP+XvQzdKbyd935sYy0bQT6Ajz7FL1g7hFiro8dqHSdPdkwei9hs8hXeV7feyTXbYmfjKQQ==} + cpu: [arm64] + os: [freebsd] + + lefthook-freebsd-x64@1.13.6: + resolution: {integrity: sha512-ai2m+Sj2kGdY46USfBrCqLKe9GYhzeq01nuyDYCrdGISePeZ6udOlD1k3lQKJGQCHb0bRz4St0r5nKDSh1x/2A==} + cpu: [x64] + os: [freebsd] + + lefthook-linux-arm64@1.13.6: + resolution: {integrity: sha512-cbo4Wtdq81GTABvikLORJsAWPKAJXE8Q5RXsICFUVznh5PHigS9dFW/4NXywo0+jfFPCT6SYds2zz4tCx6DA0Q==} + cpu: [arm64] + os: [linux] + + lefthook-linux-x64@1.13.6: + resolution: {integrity: sha512-uJl9vjCIIBTBvMZkemxCE+3zrZHlRO7Oc+nZJ+o9Oea3fu+W82jwX7a7clw8jqNfaeBS+8+ZEQgiMHWCloTsGw==} + cpu: [x64] + os: [linux] + + lefthook-openbsd-arm64@1.13.6: + resolution: {integrity: sha512-7r153dxrNRQ9ytRs2PmGKKkYdvZYFPre7My7XToSTiRu5jNCq++++eAKVkoyWPduk97dGIA+YWiEr5Noe0TK2A==} + cpu: [arm64] + os: [openbsd] + + lefthook-openbsd-x64@1.13.6: + resolution: {integrity: sha512-Z+UhLlcg1xrXOidK3aLLpgH7KrwNyWYE3yb7ITYnzJSEV8qXnePtVu8lvMBHs/myzemjBzeIr/U/+ipjclR06g==} + cpu: [x64] + os: [openbsd] + + lefthook-windows-arm64@1.13.6: + resolution: {integrity: sha512-Uxef6qoDxCmUNQwk8eBvddYJKSBFglfwAY9Y9+NnnmiHpWTjjYiObE9gT2mvGVpEgZRJVAatBXc+Ha5oDD/OgQ==} + cpu: [arm64] + os: [win32] + + lefthook-windows-x64@1.13.6: + resolution: {integrity: sha512-mOZoM3FQh3o08M8PQ/b3IYuL5oo36D9ehczIw1dAgp1Ly+Tr4fJ96A+4SEJrQuYeRD4mex9bR7Ps56I73sBSZA==} + cpu: [x64] + os: [win32] + + lefthook@1.13.6: + resolution: {integrity: sha512-ojj4/4IJ29Xn4drd5emqVgilegAPN3Kf0FQM2p/9+lwSTpU+SZ1v4Ig++NF+9MOa99UKY8bElmVrLhnUUNFh5g==} + hasBin: true + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} libsql@0.5.29: resolution: {integrity: sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg==} - cpu: [x64, arm64, wasm32, arm] os: [darwin, linux, win32] lie@3.3.0: @@ -6029,14 +6000,14 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lint-staged@16.4.0: - resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} - engines: {node: '>=20.17'} + lint-staged@17.0.7: + resolution: {integrity: sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA==} + engines: {node: '>=22.22.1'} hasBin: true - listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} + listr2@10.2.1: + resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==} + engines: {node: '>=22.13.0'} load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} @@ -6056,9 +6027,6 @@ packages: lodash-es@4.18.1: resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash.capitalize@4.2.1: resolution: {integrity: sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==} @@ -6075,27 +6043,9 @@ packages: lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.mergewith@4.6.2: - resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} - - lodash.snakecase@4.1.1: - resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - lodash.uniqby@4.7.0: resolution: {integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==} - lodash.upperfirst@4.3.1: - resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} @@ -6117,9 +6067,6 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -6145,18 +6092,25 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - lucide-react@1.8.0: - resolution: {integrity: sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw==} + lucide-react@1.17.0: + resolution: {integrity: sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + make-asynchronous@1.1.0: resolution: {integrity: sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==} engines: {node: '>=18'} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + make-fetch-happen@10.2.1: resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -6179,8 +6133,8 @@ packages: engines: {node: '>= 18'} hasBin: true - marked@18.0.2: - resolution: {integrity: sha512-NsmlUYBS/Zg57rgDWMYdnre6OTj4e+qq/JS2ot3KrYLSoHLw+sDu0Nm1ZGpRgYAq6c+b1ekaY5NzVchMCQnzcg==} + marked@18.0.5: + resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} engines: {node: '>= 20'} hasBin: true @@ -6198,6 +6152,9 @@ packages: mdast-util-from-markdown@2.0.2: resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + mdast-util-gfm-autolink-literal@2.0.1: resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} @@ -6255,6 +6212,10 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -6410,22 +6371,15 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - miniflare@4.20260421.0: - resolution: {integrity: sha512-7ZkNQ7brgQ2hh5ha9iQCDUjxBkLvuiG2VdDns9esRL8O8lXg+MoP6E0dO1rtp+ZY2I+vV1tPWr6td5IojkewLw==} - engines: {node: '>=18.0.0'} + miniflare@4.20260603.0: + resolution: {integrity: sha512-+kMQYB82gC8MPOuojHur3icQsUeZUEJ+Sphuo5rVC3Ri9txBLAW/mH33b9OVrpmkogQeaaqPS4tPtugJZhk5Kw==} + engines: {node: '>=22.0.0'} hasBin: true - minimatch@10.1.1: - resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} - engines: {node: 20 || >=22} - minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -6498,14 +6452,14 @@ packages: module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} - motion-dom@12.38.0: - resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} + motion-dom@12.40.0: + resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==} - motion-utils@12.36.0: - resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} + motion-utils@12.39.0: + resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==} - motion@12.38.0: - resolution: {integrity: sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==} + motion@12.40.0: + resolution: {integrity: sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -6524,11 +6478,6 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -6565,8 +6514,8 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@16.2.6: - resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==} + next@16.2.7: + resolution: {integrity: sha512-eMJxgjRzBaj3olkP4cBamHDXL79A8FC6u1GcsO1D1Tsx8bw/LLXUJCaoajVxtnhD3A1IJqIT8IcRJjgBIPJq4w==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -6586,21 +6535,14 @@ packages: sass: optional: true - node-abi@3.89.0: - resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} - engines: {node: '>=10'} - node-abi@3.92.0: resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} engines: {node: '>=10'} - node-abi@4.28.0: - resolution: {integrity: sha512-Qfp5XZL1cJDOabOT8H5gnqMTmM4NjvYzHp4I/Kt/Sl76OVkOBBHRFlPspGV0hYvMoqQsypFjT/Yp7Km0beXW9g==} + node-abi@4.31.0: + resolution: {integrity: sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==} engines: {node: '>=22.12.0'} - node-addon-api@1.7.2: - resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} - node-api-version@0.2.1: resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==} @@ -6617,11 +6559,14 @@ packages: encoding: optional: true - node-gyp@12.3.0: - resolution: {integrity: sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==} + node-gyp@12.4.0: + resolution: {integrity: sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + node-releases@2.0.38: resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} @@ -6749,8 +6694,9 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.2: + resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + engines: {node: '>=12.20.0'} on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} @@ -6775,11 +6721,11 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - oniguruma-parser@0.12.1: - resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} - oniguruma-to-es@4.3.4: - resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -6789,6 +6735,9 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + oxc-resolver@11.20.0: + resolution: {integrity: sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==} + p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -6805,10 +6754,6 @@ packages: resolution: {integrity: sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==} engines: {node: '>=18'} - p-is-promise@3.0.0: - resolution: {integrity: sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==} - engines: {node: '>=8'} - p-limit@1.3.0: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} engines: {node: '>=4'} @@ -6931,10 +6876,6 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - pe-library@0.4.1: resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==} engines: {node: '>=12', npm: '>=6'} @@ -6986,13 +6927,6 @@ packages: pino-abstract-transport@3.0.0: resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - pino-pretty@13.1.3: - resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} - hasBin: true - - pino-roll@4.0.0: - resolution: {integrity: sha512-axI1aQaIxXdw1F4OFFli1EDxIrdYNGLowkw/ZoZogX8oCSLHUghzwVVXUS8U+xD/Savwa5IXpiXmsSGKFX/7Sg==} - pino-std-serializers@7.1.0: resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} @@ -7008,6 +6942,20 @@ packages: resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==} engines: {node: '>=4'} + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + engines: {node: '>=16.0.0'} + + playwright-core@1.60.0: + resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.60.0: + resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==} + engines: {node: '>=18'} + hasBin: true + plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} @@ -7020,8 +6968,8 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.10: - resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -7077,8 +7025,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.7.4: - resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} engines: {node: '>=14'} hasBin: true @@ -7132,6 +7080,9 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -7139,9 +7090,6 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -7149,10 +7097,20 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} + qs@6.15.1: resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} engines: {node: '>=0.6'} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -7172,10 +7130,10 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@19.2.5: - resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: - react: ^19.2.5 + react: ^19.2.7 react-force-graph-2d@1.29.1: resolution: {integrity: sha512-1Rl/1Z3xy2iTHKj6a0jRXGyiI86xUti81K+jBQZ+Oe46csaMikp47L5AjrzA9hY9fNGD63X8ffrqnvaORukCuQ==} @@ -7218,12 +7176,6 @@ packages: '@types/react': optional: true - react-resizable-panels@4.10.0: - resolution: {integrity: sha512-frjewRQt7TCv/vCH1pJfjZ7RxAhr5pKuqVQtVgzFq/vherxBFOWyC3xMbryx5Ti2wylViGUFc93Etg4rB3E0UA==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -7234,8 +7186,8 @@ packages: '@types/react': optional: true - react@19.2.5: - resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} read-binary-file-arch@1.0.6: @@ -7376,6 +7328,10 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -7393,20 +7349,18 @@ packages: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} - rolldown@1.0.0-rc.17: - resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.61.0: - resolution: {integrity: sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -7433,9 +7387,6 @@ packages: scroll-into-view-if-needed@3.1.0: resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} - secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - semantic-release@25.0.3: resolution: {integrity: sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA==} engines: {node: ^22.14.0 || >= 24.10.0} @@ -7519,8 +7470,8 @@ packages: resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} engines: {node: '>= 0.4'} - shiki@4.0.2: - resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==} + shiki@4.2.0: + resolution: {integrity: sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==} engines: {node: '>=20'} side-channel-list@1.0.1: @@ -7567,10 +7518,6 @@ packages: resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} engines: {node: '>=8'} - slice-ansi@3.0.0: - resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} - engines: {node: '>=8'} - slice-ansi@7.1.2: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} @@ -7583,6 +7530,10 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + engines: {node: '>= 18'} + socks-proxy-agent@7.0.0: resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} engines: {node: '>= 10'} @@ -7591,9 +7542,6 @@ packages: resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sonic-boom@4.2.0: - resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} - sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -7662,9 +7610,6 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - std-env@4.1.0: resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} @@ -7687,8 +7632,8 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.0: - resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} string_decoder@1.1.1: @@ -7704,10 +7649,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} @@ -7732,19 +7673,12 @@ packages: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - strip-json-comments@5.0.3: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - - stripe@22.0.2: - resolution: {integrity: sha512-2/BLrQ3oB1zlNfeL/LfHFjTGx6EQn0j+ztrrTJHuDjV5VVIpk92oSDaxyKLUr3pG3dnee2LZqhFUv2Bf0G1/3g==} + stripe@22.2.0: + resolution: {integrity: sha512-WFGpMOom9QZqso1kcnSwJsCdC1QHDlMoCOxBZRf3JraMzhkfw7dgSdD2a1CFZrqC+mzAfqeEtYILrZhWKIDruA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -7802,16 +7736,16 @@ packages: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} - tailwind-merge@3.5.0: - resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@4.2.4: - resolution: {integrity: sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==} + tailwindcss@4.3.0: + resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} tapable@2.3.3: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} @@ -7829,8 +7763,8 @@ packages: engines: {node: '>=10'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - tar@7.5.13: - resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + tar@7.5.16: + resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} engines: {node: '>=18'} temp-dir@3.0.0: @@ -7878,21 +7812,10 @@ packages: tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - - tinyexec@1.1.1: - resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} - engines: {node: '>=18'} - tinyexec@1.2.4: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} @@ -7901,27 +7824,15 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} - tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} - engines: {node: '>=14.0.0'} - tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} - tmp@0.2.5: - resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} engines: {node: '>=14.14'} to-buffer@1.2.2: @@ -7966,6 +7877,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.22.4: + resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} + engines: {node: '>=18.0.0'} + hasBin: true + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -7973,8 +7889,8 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - turbo@2.9.14: - resolution: {integrity: sha512-BQqXRr4UoWI3UPFrtznCLykYHxwxWh53iCB57x092jPMjIlW1wnm3N895g5irpiXmnxUhREBB0n6+y8BHhs4nw==} + turbo@2.9.16: + resolution: {integrity: sha512-NqgRQy6j6dPYcdSdv0q1g9QsZg7SWg87RERM8otw/1AtKU2yTFVClOM7cbwKzOonZr/Ek1blTBucw64L9H0Bwg==} hasBin: true turndown-plugin-gfm@1.0.2: @@ -8008,6 +7924,10 @@ packages: resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} engines: {node: '>=20'} + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -8016,8 +7936,8 @@ packages: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - typescript-eslint@8.59.0: - resolution: {integrity: sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==} + typescript-eslint@8.60.1: + resolution: {integrity: sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -8028,35 +7948,45 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} hasBin: true + unbash@2.2.0: + resolution: {integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==} + engines: {node: '>=14'} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici-types@7.19.2: - resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@6.25.0: - resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} + undici@6.26.0: + resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} engines: {node: '>=18.17'} - undici@7.18.2: - resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} - engines: {node: '>=20.18.1'} - undici@7.24.8: resolution: {integrity: sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==} engines: {node: '>=20.18.1'} + undici@7.25.0: + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} + engines: {node: '>=20.18.1'} + + undici@7.27.2: + resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} + engines: {node: '>=20.18.1'} + unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} @@ -8136,6 +8066,9 @@ packages: unzip-crx-3@0.2.0: resolution: {integrity: sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==} + unzipper@0.12.3: + resolution: {integrity: sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -8187,10 +8120,6 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - verror@1.10.1: - resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} - engines: {node: '>=0.6.0'} - vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -8200,58 +8129,13 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - - vite@7.3.5: - resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vite@8.0.10: - resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.1.0 + '@vitejs/devtools': ^0.1.18 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -8288,49 +8172,23 @@ packages: yaml: optional: true - vitest@3.2.6: - resolution: {integrity: sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.6 - '@vitest/ui': 3.2.6 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/debug': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vitest@4.1.0: - resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + vitest@4.1.8: + resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0 - '@vitest/browser-preview': 4.1.0 - '@vitest/browser-webdriverio': 4.1.0 - '@vitest/ui': 4.1.0 + '@vitest/browser-playwright': 4.1.8 + '@vitest/browser-preview': 4.1.8 + '@vitest/browser-webdriverio': 4.1.8 + '@vitest/coverage-istanbul': 4.1.8 + '@vitest/coverage-v8': 4.1.8 + '@vitest/ui': 4.1.8 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -8344,6 +8202,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -8354,6 +8216,10 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -8363,14 +8229,17 @@ packages: web-worker@1.5.0: resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} + webcrypto-core@1.9.2: + resolution: {integrity: sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} which@2.0.2: @@ -8405,21 +8274,25 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - workerd@1.20260421.1: - resolution: {integrity: sha512-zTYD+xFR4d7TUCxsyl7FTPth9a8CDgk8pM7xUWbJxo0SGUx+2e5C7Q5LrramBZwmuAErtzXmOjlQ15PtkPAhZA==} + workerd@1.20260603.1: + resolution: {integrity: sha512-NPcbhI1++CS+fnELyXtsIR52en+5kwr/OrKeiQeYXGy10HxmPdsQBv9N+DU7hJIOOmBHhOGAAsoGDjyiQ2YCaA==} engines: {node: '>=16'} hasBin: true - wrangler@4.84.1: - resolution: {integrity: sha512-Xe1S/Bik7pNdtdJ+asHsEZC2dX9k3WxYn2BbxFtOrrLVxN/LKi750zsrjX41jSAk00M/O1l7jzyQV4sQqw8ftg==} - engines: {node: '>=20.3.0'} + wrangler@4.98.0: + resolution: {integrity: sha512-cXfFUuF4rMIvE0hiMnXjEAB27ERryaCgquBJdUoPIjFzYYE1rbRdMUkEdQ18qDPUtsPvhJdqxLntixT9OfSzQw==} + engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260421.1 + '@cloudflare/workers-types': ^4.20260603.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -8435,8 +8308,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -8447,8 +8320,8 @@ packages: utf-8-validate: optional: true - ws@8.20.0: - resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -8484,8 +8357,8 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} hasBin: true @@ -8535,11 +8408,11 @@ packages: peerDependencies: zod: ^3.25.28 || ^4 - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zustand@5.0.12: - resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} + zustand@5.0.14: + resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -8575,7 +8448,7 @@ snapshots: '@actions/http-client@4.0.0': dependencies: tunnel: 0.0.6 - undici: 6.25.0 + undici: 6.26.0 '@actions/io@3.0.2': {} @@ -8751,8 +8624,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.29.2': @@ -8764,6 +8641,10 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -8792,43 +8673,50 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@cloudflare/kv-asset-handler@0.4.2': {} + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@1.0.2': {} + + '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/unenv-preset@2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260421.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260603.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260421.1 + workerd: 1.20260603.1 - '@cloudflare/workerd-darwin-64@1.20260421.1': + '@cloudflare/workerd-darwin-64@1.20260603.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260421.1': + '@cloudflare/workerd-darwin-arm64@1.20260603.1': optional: true - '@cloudflare/workerd-linux-64@1.20260421.1': + '@cloudflare/workerd-linux-64@1.20260603.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260421.1': + '@cloudflare/workerd-linux-arm64@1.20260603.1': optional: true - '@cloudflare/workerd-windows-64@1.20260421.1': + '@cloudflare/workerd-windows-64@1.20260603.1': optional: true - '@cloudflare/workers-types@4.20260423.1': {} + '@cloudflare/workers-types@4.20260608.1': {} - '@codemirror/autocomplete@6.20.1': + '@codemirror/autocomplete@6.20.3': dependencies: '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.2 '@codemirror/commands@6.10.3': dependencies: '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.2 '@codemirror/lang-angular@0.1.4': @@ -8847,7 +8735,7 @@ snapshots: '@codemirror/lang-css@6.3.1': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 '@lezer/common': 1.5.2 @@ -8855,7 +8743,7 @@ snapshots: '@codemirror/lang-go@6.0.1': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 '@lezer/common': 1.5.2 @@ -8863,12 +8751,12 @@ snapshots: '@codemirror/lang-html@6.4.11': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/lang-css': 6.3.1 '@codemirror/lang-javascript': 6.2.4 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.2 '@lezer/css': 1.3.0 '@lezer/html': 1.3.13 @@ -8880,11 +8768,11 @@ snapshots: '@codemirror/lang-javascript@6.2.4': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.3 '@codemirror/lint': 6.9.2 '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.2 '@lezer/javascript': 1.5.4 @@ -8911,22 +8799,22 @@ snapshots: '@codemirror/lang-liquid@6.3.1': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/lang-html': 6.4.11 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.2 '@lezer/highlight': 1.2.3 '@lezer/lr': 1.4.10 '@codemirror/lang-markdown@6.5.0': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/lang-html': 6.4.11 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.0 '@lezer/markdown': 1.6.2 @@ -8940,7 +8828,7 @@ snapshots: '@codemirror/lang-python@6.2.1': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 '@lezer/common': 1.5.2 @@ -8961,7 +8849,7 @@ snapshots: '@codemirror/lang-sql@6.10.0': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 '@lezer/common': 1.5.2 @@ -8986,16 +8874,16 @@ snapshots: '@codemirror/lang-xml@6.1.0': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.2 '@lezer/xml': 1.0.6 '@codemirror/lang-yaml@6.1.2': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.3 '@codemirror/state': 6.6.0 '@lezer/common': 1.5.2 @@ -9032,7 +8920,7 @@ snapshots: '@codemirror/language@6.12.3': dependencies: '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 '@lezer/common': 1.5.2 '@lezer/highlight': 1.2.3 '@lezer/lr': 1.4.10 @@ -9045,14 +8933,14 @@ snapshots: '@codemirror/lint@6.9.2': dependencies: '@codemirror/state': 6.6.0 - '@codemirror/view': 6.41.1 + '@codemirror/view': 6.43.0 crelt: 1.0.6 '@codemirror/state@6.6.0': dependencies: '@marijn/find-cluster-break': 1.0.2 - '@codemirror/view@6.41.1': + '@codemirror/view@6.43.0': dependencies: '@codemirror/state': 6.6.0 crelt: 1.0.6 @@ -9062,116 +8950,110 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@commitlint/cli@20.5.0(@types/node@25.6.0)(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)(typescript@5.9.3)': + '@commitlint/cli@21.0.2(@types/node@25.9.2)(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)(typescript@6.0.3)': dependencies: - '@commitlint/format': 20.5.0 - '@commitlint/lint': 20.5.0 - '@commitlint/load': 20.5.0(@types/node@25.6.0)(typescript@5.9.3) - '@commitlint/read': 20.5.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0) - '@commitlint/types': 20.5.0 - tinyexec: 1.1.1 - yargs: 17.7.2 + '@commitlint/format': 21.0.1 + '@commitlint/lint': 21.0.2 + '@commitlint/load': 21.0.2(@types/node@25.9.2)(typescript@6.0.3) + '@commitlint/read': 21.0.2(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0) + '@commitlint/types': 21.0.1 + tinyexec: 1.2.4 + yargs: 18.0.0 transitivePeerDependencies: - '@types/node' - conventional-commits-filter - conventional-commits-parser - typescript - '@commitlint/config-conventional@20.5.0': + '@commitlint/config-conventional@21.0.2': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 21.0.1 conventional-changelog-conventionalcommits: 9.3.1 - '@commitlint/config-validator@20.5.0': + '@commitlint/config-validator@21.0.1': dependencies: - '@commitlint/types': 20.5.0 - ajv: 8.18.0 + '@commitlint/types': 21.0.1 + ajv: 8.20.0 - '@commitlint/ensure@20.5.0': + '@commitlint/ensure@21.0.1': dependencies: - '@commitlint/types': 20.5.0 - lodash.camelcase: 4.3.0 - lodash.kebabcase: 4.1.1 - lodash.snakecase: 4.1.1 - lodash.startcase: 4.4.0 - lodash.upperfirst: 4.3.1 + '@commitlint/types': 21.0.1 + es-toolkit: 1.47.0 - '@commitlint/execute-rule@20.0.0': {} + '@commitlint/execute-rule@21.0.1': {} - '@commitlint/format@20.5.0': + '@commitlint/format@21.0.1': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 21.0.1 picocolors: 1.1.1 - '@commitlint/is-ignored@20.5.0': + '@commitlint/is-ignored@21.0.2': dependencies: - '@commitlint/types': 20.5.0 - semver: 7.8.0 + '@commitlint/types': 21.0.1 + semver: 7.8.2 - '@commitlint/lint@20.5.0': + '@commitlint/lint@21.0.2': dependencies: - '@commitlint/is-ignored': 20.5.0 - '@commitlint/parse': 20.5.0 - '@commitlint/rules': 20.5.0 - '@commitlint/types': 20.5.0 + '@commitlint/is-ignored': 21.0.2 + '@commitlint/parse': 21.0.2 + '@commitlint/rules': 21.0.2 + '@commitlint/types': 21.0.1 - '@commitlint/load@20.5.0(@types/node@25.6.0)(typescript@5.9.3)': + '@commitlint/load@21.0.2(@types/node@25.9.2)(typescript@6.0.3)': dependencies: - '@commitlint/config-validator': 20.5.0 - '@commitlint/execute-rule': 20.0.0 - '@commitlint/resolve-extends': 20.5.0 - '@commitlint/types': 20.5.0 - cosmiconfig: 9.0.1(typescript@5.9.3) - cosmiconfig-typescript-loader: 6.3.0(@types/node@25.6.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) + '@commitlint/config-validator': 21.0.1 + '@commitlint/execute-rule': 21.0.1 + '@commitlint/resolve-extends': 21.0.1 + '@commitlint/types': 21.0.1 + cosmiconfig: 9.0.2(typescript@6.0.3) + cosmiconfig-typescript-loader: 6.3.0(@types/node@25.9.2)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3) + es-toolkit: 1.47.0 is-plain-obj: 4.1.0 - lodash.mergewith: 4.6.2 picocolors: 1.1.1 transitivePeerDependencies: - '@types/node' - typescript - '@commitlint/message@20.4.3': {} + '@commitlint/message@21.0.2': {} - '@commitlint/parse@20.5.0': + '@commitlint/parse@21.0.2': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 21.0.1 conventional-changelog-angular: 8.3.1 conventional-commits-parser: 6.4.0 - '@commitlint/read@20.5.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)': + '@commitlint/read@21.0.2(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)': dependencies: - '@commitlint/top-level': 20.4.3 - '@commitlint/types': 20.5.0 + '@commitlint/top-level': 21.0.2 + '@commitlint/types': 21.0.1 git-raw-commits: 5.0.1(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0) - minimist: 1.2.8 tinyexec: 1.2.4 transitivePeerDependencies: - conventional-commits-filter - conventional-commits-parser - '@commitlint/resolve-extends@20.5.0': + '@commitlint/resolve-extends@21.0.1': dependencies: - '@commitlint/config-validator': 20.5.0 - '@commitlint/types': 20.5.0 - global-directory: 4.0.1 - import-meta-resolve: 4.2.0 - lodash.mergewith: 4.6.2 + '@commitlint/config-validator': 21.0.1 + '@commitlint/types': 21.0.1 + es-toolkit: 1.47.0 + global-directory: 5.0.0 resolve-from: 5.0.0 - '@commitlint/rules@20.5.0': + '@commitlint/rules@21.0.2': dependencies: - '@commitlint/ensure': 20.5.0 - '@commitlint/message': 20.4.3 - '@commitlint/to-lines': 20.0.0 - '@commitlint/types': 20.5.0 + '@commitlint/ensure': 21.0.1 + '@commitlint/message': 21.0.2 + '@commitlint/to-lines': 21.0.1 + '@commitlint/types': 21.0.1 - '@commitlint/to-lines@20.0.0': {} + '@commitlint/to-lines@21.0.1': {} - '@commitlint/top-level@20.4.3': + '@commitlint/top-level@21.0.2': dependencies: escalade: 3.2.0 - '@commitlint/types@20.5.0': + '@commitlint/types@21.0.1': dependencies: conventional-commits-parser: 6.4.0 picocolors: 1.1.1 @@ -9180,7 +9062,7 @@ snapshots: dependencies: '@simple-libs/child-process-utils': 1.0.2 '@simple-libs/stream-utils': 1.2.0 - semver: 7.8.0 + semver: 7.8.2 optionalDependencies: conventional-commits-filter: 5.0.0 conventional-commits-parser: 6.4.0 @@ -9214,7 +9096,7 @@ snapshots: fs-extra: 9.1.0 minimist: 1.2.8 - '@electron/get@2.0.3': + '@electron/get@3.1.0': dependencies: debug: 4.4.3 env-paths: 2.2.1 @@ -9228,21 +9110,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/get@3.1.0': + '@electron/get@5.0.0': dependencies: debug: 4.4.3 - env-paths: 2.2.1 - fs-extra: 8.1.0 - got: 11.8.6 + env-paths: 3.0.0 + graceful-fs: 4.2.11 progress: 2.0.3 - semver: 6.3.1 + semver: 7.8.2 sumchecker: 3.0.1 optionalDependencies: - global-agent: 3.0.0 + undici: 7.27.2 transitivePeerDependencies: - supports-color - '@electron/node-gyp@git+https://git@github.com:electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2': + '@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2': dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.3 @@ -9290,7 +9171,7 @@ snapshots: '@electron/rebuild@3.7.0': dependencies: - '@electron/node-gyp': git+https://git@github.com:electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2 + '@electron/node-gyp': https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2 '@malept/cross-spawn-promise': 2.0.0 chalk: 4.1.2 debug: 4.4.3 @@ -9312,9 +9193,9 @@ snapshots: dependencies: '@malept/cross-spawn-promise': 2.0.0 debug: 4.4.3 - node-abi: 4.28.0 + node-abi: 4.31.0 node-api-version: 0.2.1 - node-gyp: 12.3.0 + node-gyp: 12.4.0 read-binary-file-arch: 1.0.6 transitivePeerDependencies: - supports-color @@ -9337,7 +9218,7 @@ snapshots: '@malept/cross-spawn-promise': 2.0.0 debug: 4.4.3 dir-compare: 4.2.0 - fs-extra: 11.3.4 + fs-extra: 11.3.5 minimatch: 9.0.9 plist: 3.1.0 transitivePeerDependencies: @@ -9360,16 +9241,32 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.0': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.0': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild-kit/core-utils@3.3.2': dependencies: esbuild: 0.18.20 @@ -9680,91 +9577,75 @@ snapshots: '@esbuild/win32-x64@0.28.0': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.4.1(jiti@2.7.0))': dependencies: - eslint: 9.39.2(jiti@2.6.1) + eslint: 10.4.1(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.1': + '@eslint/config-array@0.23.5': dependencies: - '@eslint/object-schema': 2.1.7 + '@eslint/object-schema': 3.0.5 debug: 4.4.3 - minimatch: 3.1.2 + minimatch: 10.2.5 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.4.2': + '@eslint/config-helpers@0.6.0': dependencies: - '@eslint/core': 0.17.0 + '@eslint/core': 1.2.1 - '@eslint/core@0.17.0': + '@eslint/core@1.2.1': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.3': - dependencies: - ajv: 6.15.0 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@10.0.1(eslint@9.39.2(jiti@2.6.1))': + '@eslint/js@10.0.1(eslint@10.4.1(jiti@2.7.0))': optionalDependencies: - eslint: 9.39.2(jiti@2.6.1) - - '@eslint/js@9.39.2': {} + eslint: 10.4.1(jiti@2.7.0) - '@eslint/object-schema@2.1.7': {} + '@eslint/object-schema@3.0.5': {} - '@eslint/plugin-kit@0.4.1': + '@eslint/plugin-kit@0.7.2': dependencies: - '@eslint/core': 0.17.0 + '@eslint/core': 1.2.1 levn: 0.4.1 '@fastify/otel@0.18.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.212.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 minimatch: 10.2.5 transitivePeerDependencies: - supports-color - '@floating-ui/core@1.7.4': + '@floating-ui/core@1.7.5': dependencies: - '@floating-ui/utils': 0.2.10 + '@floating-ui/utils': 0.2.11 - '@floating-ui/dom@1.7.5': + '@floating-ui/dom@1.7.6': dependencies: - '@floating-ui/core': 1.7.4 - '@floating-ui/utils': 0.2.10 + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@floating-ui/dom': 1.7.5 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@floating-ui/dom': 1.7.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - '@floating-ui/utils@0.2.10': {} + '@floating-ui/utils@0.2.11': {} '@fontsource-variable/jetbrains-mono@5.2.8': {} '@fontsource/inter@5.2.8': {} - '@fumadocs/tailwind@0.0.5(@tailwindcss/oxide@4.2.4)(tailwindcss@4.2.4)': + '@fumadocs/tailwind@0.0.5(@tailwindcss/oxide@4.3.0)(tailwindcss@4.3.0)': optionalDependencies: - '@tailwindcss/oxide': 4.2.4 - tailwindcss: 4.2.4 + '@tailwindcss/oxide': 4.3.0 + tailwindcss: 4.3.0 '@gar/promisify@1.1.3': {} @@ -9772,18 +9653,23 @@ snapshots: dependencies: hono: 4.12.23 - '@hono/zod-validator@0.7.6(hono@4.12.23)(zod@4.3.6)': + '@hono/zod-validator@0.8.0(hono@4.12.23)(zod@4.4.3)': dependencies: hono: 4.12.23 - zod: 4.3.6 + zod: 4.4.3 - '@humanfs/core@0.19.1': {} + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 - '@humanfs/node@0.16.7': + '@humanfs/node@0.16.8': dependencies: - '@humanfs/core': 0.19.1 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 '@humanwhocodes/retry': 0.4.3 + '@humanfs/types@0.15.0': {} + '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} @@ -9872,7 +9758,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -9884,12 +9770,6 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.0': - dependencies: - '@isaacs/balanced-match': 4.0.1 - '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -10097,11 +9977,11 @@ snapshots: '@mdx-js/mdx@3.1.1': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 - '@types/mdx': 2.0.13 - acorn: 8.15.0 + '@types/mdx': 2.0.14 + acorn: 8.16.0 collapse-white-space: 2.1.0 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 @@ -10110,7 +9990,7 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 markdown-extensions: 2.0.0 recma-build-jsx: 1.0.0 - recma-jsx: 1.0.1(acorn@8.15.0) + recma-jsx: 1.0.1(acorn@8.16.0) recma-stringify: 1.0.0 rehype-recma: 1.0.0 remark-mdx: 3.1.1 @@ -10127,7 +10007,7 @@ snapshots: '@mixmark-io/domino@2.2.0': {} - '@modelcontextprotocol/sdk@1.29.0(zod@4.3.6)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.23) ajv: 8.18.0 @@ -10140,27 +10020,27 @@ snapshots: express: 5.2.1 express-rate-limit: 8.4.0(express@5.2.1) hono: 4.12.23 - jose: 6.2.2 + jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 4.3.6 - zod-to-json-schema: 3.25.2(zod@4.3.6) + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) transitivePeerDependencies: - supports-color '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@tybys/wasm-util': 0.10.2 optional: true '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 + '@tybys/wasm-util': 0.10.2 optional: true '@neon-rs/load@0.0.4': {} @@ -10170,34 +10050,50 @@ snapshots: '@types/pg': 8.11.6 optional: true - '@next/env@16.2.6': {} + '@next/env@16.2.7': {} - '@next/swc-darwin-arm64@16.2.6': + '@next/swc-darwin-arm64@16.2.7': optional: true - '@next/swc-darwin-x64@16.2.6': + '@next/swc-darwin-x64@16.2.7': optional: true - '@next/swc-linux-arm64-gnu@16.2.6': + '@next/swc-linux-arm64-gnu@16.2.7': optional: true - '@next/swc-linux-arm64-musl@16.2.6': + '@next/swc-linux-arm64-musl@16.2.7': optional: true - '@next/swc-linux-x64-gnu@16.2.6': + '@next/swc-linux-x64-gnu@16.2.7': optional: true - '@next/swc-linux-x64-musl@16.2.6': + '@next/swc-linux-x64-musl@16.2.7': optional: true - '@next/swc-win32-arm64-msvc@16.2.6': + '@next/swc-win32-arm64-msvc@16.2.7': optional: true - '@next/swc-win32-x64-msvc@16.2.6': + '@next/swc-win32-x64-msvc@16.2.7': optional: true '@noble/ed25519@3.1.0': {} + '@noble/hashes@1.4.0': {} + + '@noble/hashes@2.2.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + '@npmcli/fs@2.1.2': dependencies: '@gar/promisify': 1.1.3 @@ -10261,7 +10157,7 @@ snapshots: '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 fast-content-type-parse: 3.0.0 - json-with-bigint: 3.5.7 + json-with-bigint: 3.5.8 universal-user-agent: 7.0.3 '@octokit/types@16.0.0': @@ -10282,16 +10178,12 @@ snapshots: '@opentelemetry/api@1.9.1': {} - '@opentelemetry/context-async-hooks@2.7.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.7.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.40.0 @@ -10299,7 +10191,7 @@ snapshots: '@opentelemetry/instrumentation-amqplib@0.61.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -10308,7 +10200,7 @@ snapshots: '@opentelemetry/instrumentation-connect@0.57.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 '@types/connect': 3.4.38 @@ -10322,19 +10214,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-express@0.62.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.40.0 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation-fs@0.33.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) transitivePeerDependencies: - supports-color @@ -10356,7 +10239,7 @@ snapshots: '@opentelemetry/instrumentation-hapi@0.60.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -10400,7 +10283,7 @@ snapshots: '@opentelemetry/instrumentation-koa@0.62.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -10424,7 +10307,7 @@ snapshots: '@opentelemetry/instrumentation-mongoose@0.60.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -10451,7 +10334,7 @@ snapshots: '@opentelemetry/instrumentation-pg@0.66.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.1) @@ -10478,15 +10361,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-undici@0.24.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.40.0 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation@0.207.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -10516,17 +10390,17 @@ snapshots: '@opentelemetry/redis-common@0.38.3': {} - '@opentelemetry/resources@2.7.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/sdk-trace-base@2.7.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/semantic-conventions@1.40.0': {} @@ -10534,30 +10408,119 @@ snapshots: '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@orama/orama@3.1.18': {} - '@oxc-project/types@0.127.0': {} + '@oxc-project/types@0.133.0': {} - '@petamoriken/float16@3.9.3': + '@oxc-resolver/binding-android-arm-eabi@11.20.0': optional: true - '@pinojs/redact@0.4.0': {} + '@oxc-resolver/binding-android-arm64@11.20.0': + optional: true - '@pkgjs/parseargs@0.11.0': + '@oxc-resolver/binding-darwin-arm64@11.20.0': optional: true - '@pnpm/config.env-replace@1.1.0': {} + '@oxc-resolver/binding-darwin-x64@11.20.0': + optional: true - '@pnpm/network.ca-file@1.0.2': - dependencies: - graceful-fs: 4.2.10 + '@oxc-resolver/binding-freebsd-x64@11.20.0': + optional: true - '@pnpm/npm-conf@3.0.2': - dependencies: - '@pnpm/config.env-replace': 1.1.0 - '@pnpm/network.ca-file': 1.0.2 + '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.20.0': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.20.0': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.20.0': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.20.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.20.0': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.20.0': + optional: true + + '@package-json/types@0.0.12': {} + + '@peculiar/asn1-schema@2.7.0': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/json-schema@1.1.12': + dependencies: + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/webcrypto@1.7.1': + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/json-schema': 1.1.12 + '@peculiar/utils': 2.0.3 + tslib: 2.8.1 + webcrypto-core: 1.9.2 + + '@petamoriken/float16@3.9.3': + optional: true + + '@pinojs/redact@0.4.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@playwright/test@1.60.0': + dependencies: + playwright: 1.60.0 + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@3.0.2': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 '@poppinss/colors@4.1.6': @@ -10579,519 +10542,425 @@ snapshots: transitivePeerDependencies: - supports-color - '@radix-ui/number@1.1.1': {} + '@radix-ui/number@1.1.2': {} - '@radix-ui/primitive@1.1.3': {} + '@radix-ui/primitive@1.1.4': {} - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-accordion@1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collapsible': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-arrow@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collapsible@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-collection@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 - - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) + '@types/react': 19.2.17 + + '@radix-ui/react-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-dismissable-layer@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-focus-scope@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 - - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@types/react': 19.2.17 + + '@radix-ui/react-navigation-menu@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popover@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@floating-ui/react-dom': 2.1.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/rect': 1.1.1 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popper@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-portal@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-primitive@2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-roving-focus@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-scroll-area@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-separator@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-slot@1.2.5(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 - - '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.5)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - optionalDependencies: - '@types/react': 19.2.14 - - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@types/react': 19.2.17 + + '@radix-ui/react-tabs@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.2.5 + '@radix-ui/rect': 1.1.2 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-visually-hidden@1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/rect@1.1.1': {} + '@radix-ui/rect@1.1.2': {} - '@rolldown/binding-android-arm64@1.0.0-rc.17': + '@rolldown/binding-android-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + '@rolldown/binding-darwin-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.17': + '@rolldown/binding-darwin-x64@1.0.3': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + '@rolldown/binding-freebsd-x64@1.0.3': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-arm64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + '@rolldown/binding-linux-arm64-musl@1.0.3': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-ppc64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-s390x-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + '@rolldown/binding-linux-x64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + '@rolldown/binding-linux-x64-musl@1.0.3': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + '@rolldown/binding-openharmony-arm64@1.0.3': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + '@rolldown/binding-wasm32-wasi@1.0.3': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': - optional: true - - '@rolldown/pluginutils@1.0.0-rc.17': {} - - '@rolldown/pluginutils@1.0.0-rc.7': {} - - '@rollup/rollup-android-arm-eabi@4.61.0': - optional: true - - '@rollup/rollup-android-arm64@4.61.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.61.0': - optional: true - - '@rollup/rollup-darwin-x64@4.61.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.61.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.61.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.61.0': + '@rolldown/binding-win32-arm64-msvc@1.0.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.61.0': + '@rolldown/binding-win32-x64-msvc@1.0.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.61.0': - optional: true - - '@rollup/rollup-openbsd-x64@4.61.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.61.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.61.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.61.0': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.61.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.61.0': - optional: true + '@rolldown/pluginutils@1.0.1': {} '@sec-ant/readable-stream@0.4.1': {} - '@semantic-release/changelog@6.0.3(semantic-release@25.0.3(typescript@5.9.3))': + '@semantic-release/changelog@6.0.3(semantic-release@25.0.3(typescript@6.0.3))': dependencies: '@semantic-release/error': 3.0.0 aggregate-error: 3.1.0 fs-extra: 11.3.4 lodash: 4.17.23 - semantic-release: 25.0.3(typescript@5.9.3) + semantic-release: 25.0.3(typescript@6.0.3) - '@semantic-release/commit-analyzer@13.0.1(semantic-release@25.0.3(typescript@5.9.3))': + '@semantic-release/commit-analyzer@13.0.1(semantic-release@25.0.3(typescript@6.0.3))': dependencies: conventional-changelog-angular: 8.3.0 conventional-changelog-writer: 8.4.0 @@ -11101,7 +10970,7 @@ snapshots: import-from-esm: 2.0.0 lodash-es: 4.17.22 micromatch: 4.0.8 - semantic-release: 25.0.3(typescript@5.9.3) + semantic-release: 25.0.3(typescript@6.0.3) transitivePeerDependencies: - supports-color @@ -11109,7 +10978,7 @@ snapshots: '@semantic-release/error@4.0.0': {} - '@semantic-release/exec@7.1.0(semantic-release@25.0.3(typescript@5.9.3))': + '@semantic-release/exec@7.1.0(semantic-release@25.0.3(typescript@6.0.3))': dependencies: '@semantic-release/error': 4.0.0 aggregate-error: 3.1.0 @@ -11117,11 +10986,11 @@ snapshots: execa: 9.6.1 lodash-es: 4.17.22 parse-json: 8.3.0 - semantic-release: 25.0.3(typescript@5.9.3) + semantic-release: 25.0.3(typescript@6.0.3) transitivePeerDependencies: - supports-color - '@semantic-release/git@10.0.1(semantic-release@25.0.3(typescript@5.9.3))': + '@semantic-release/git@10.0.1(semantic-release@25.0.3(typescript@6.0.3))': dependencies: '@semantic-release/error': 3.0.0 aggregate-error: 3.1.0 @@ -11131,11 +11000,11 @@ snapshots: lodash: 4.17.23 micromatch: 4.0.8 p-reduce: 2.1.0 - semantic-release: 25.0.3(typescript@5.9.3) + semantic-release: 25.0.3(typescript@6.0.3) transitivePeerDependencies: - supports-color - '@semantic-release/github@12.0.6(semantic-release@25.0.3(typescript@5.9.3))': + '@semantic-release/github@12.0.8(semantic-release@25.0.3(typescript@6.0.3))': dependencies: '@octokit/core': 7.0.6 '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) @@ -11145,27 +11014,27 @@ snapshots: aggregate-error: 5.0.0 debug: 4.4.3 dir-glob: 3.0.1 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - issue-parser: 7.0.1 - lodash-es: 4.17.22 + http-proxy-agent: 9.0.0 + https-proxy-agent: 9.0.0 + issue-parser: 7.0.2 + lodash-es: 4.18.1 mime: 4.1.0 p-filter: 4.1.0 - semantic-release: 25.0.3(typescript@5.9.3) - tinyglobby: 0.2.15 - undici: 7.18.2 + semantic-release: 25.0.3(typescript@6.0.3) + tinyglobby: 0.2.16 + undici: 7.25.0 url-join: 5.0.0 transitivePeerDependencies: - supports-color - '@semantic-release/npm@13.1.5(semantic-release@25.0.3(typescript@5.9.3))': + '@semantic-release/npm@13.1.5(semantic-release@25.0.3(typescript@6.0.3))': dependencies: '@actions/core': 3.0.0 '@semantic-release/error': 4.0.0 aggregate-error: 5.0.0 env-ci: 11.2.0 execa: 9.6.1 - fs-extra: 11.3.4 + fs-extra: 11.3.5 lodash-es: 4.17.22 nerf-dart: 1.0.0 normalize-url: 9.0.0 @@ -11173,88 +11042,82 @@ snapshots: rc: 1.2.8 read-pkg: 10.1.0 registry-auth-token: 5.1.1 - semantic-release: 25.0.3(typescript@5.9.3) - semver: 7.7.3 + semantic-release: 25.0.3(typescript@6.0.3) + semver: 7.8.2 tempy: 3.2.0 - '@semantic-release/release-notes-generator@14.1.0(semantic-release@25.0.3(typescript@5.9.3))': + '@semantic-release/release-notes-generator@14.1.1(semantic-release@25.0.3(typescript@6.0.3))': dependencies: - conventional-changelog-angular: 8.3.0 + conventional-changelog-angular: 8.3.1 conventional-changelog-writer: 8.4.0 conventional-commits-filter: 5.0.0 - conventional-commits-parser: 6.3.0 + conventional-commits-parser: 6.4.0 debug: 4.4.3 - get-stream: 7.0.1 import-from-esm: 2.0.0 - into-stream: 7.0.0 - lodash-es: 4.17.22 + lodash-es: 4.18.1 read-package-up: 11.0.0 - semantic-release: 25.0.3(typescript@5.9.3) + semantic-release: 25.0.3(typescript@6.0.3) transitivePeerDependencies: - supports-color - '@sentry-internal/browser-utils@10.47.0': + '@sentry-internal/browser-utils@10.50.0': dependencies: - '@sentry/core': 10.47.0 + '@sentry/core': 10.50.0 - '@sentry-internal/feedback@10.47.0': + '@sentry-internal/feedback@10.50.0': dependencies: - '@sentry/core': 10.47.0 + '@sentry/core': 10.50.0 - '@sentry-internal/replay-canvas@10.47.0': + '@sentry-internal/replay-canvas@10.50.0': dependencies: - '@sentry-internal/replay': 10.47.0 - '@sentry/core': 10.47.0 + '@sentry-internal/replay': 10.50.0 + '@sentry/core': 10.50.0 - '@sentry-internal/replay@10.47.0': + '@sentry-internal/replay@10.50.0': dependencies: - '@sentry-internal/browser-utils': 10.47.0 - '@sentry/core': 10.47.0 + '@sentry-internal/browser-utils': 10.50.0 + '@sentry/core': 10.50.0 - '@sentry/browser@10.47.0': + '@sentry/browser@10.50.0': dependencies: - '@sentry-internal/browser-utils': 10.47.0 - '@sentry-internal/feedback': 10.47.0 - '@sentry-internal/replay': 10.47.0 - '@sentry-internal/replay-canvas': 10.47.0 - '@sentry/core': 10.47.0 + '@sentry-internal/browser-utils': 10.50.0 + '@sentry-internal/feedback': 10.50.0 + '@sentry-internal/replay': 10.50.0 + '@sentry-internal/replay-canvas': 10.50.0 + '@sentry/core': 10.50.0 - '@sentry/core@10.47.0': {} + '@sentry/core@10.50.0': {} - '@sentry/electron@7.11.0': + '@sentry/electron@7.13.0': dependencies: - '@sentry/browser': 10.47.0 - '@sentry/core': 10.47.0 - '@sentry/node': 10.47.0 + '@sentry/browser': 10.50.0 + '@sentry/core': 10.50.0 + '@sentry/node': 10.50.0 transitivePeerDependencies: - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/node-core@10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': + '@sentry/node-core@10.50.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: - '@sentry/core': 10.47.0 - '@sentry/opentelemetry': 10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/core': 10.50.0 + '@sentry/opentelemetry': 10.50.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) import-in-the-middle: 3.0.1 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@sentry/node@10.47.0': + '@sentry/node@10.50.0': dependencies: '@fastify/otel': 0.18.0(@opentelemetry/api@1.9.1) '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-amqplib': 0.61.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-connect': 0.57.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-dataloader': 0.31.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-express': 0.62.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-fs': 0.33.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-generic-pool': 0.57.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-graphql': 0.62.0(@opentelemetry/api@1.9.1) @@ -11272,62 +11135,59 @@ snapshots: '@opentelemetry/instrumentation-pg': 0.66.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-redis': 0.62.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-tedious': 0.33.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-undici': 0.24.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 '@prisma/instrumentation': 7.6.0(@opentelemetry/api@1.9.1) - '@sentry/core': 10.47.0 - '@sentry/node-core': 10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) - '@sentry/opentelemetry': 10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/core': 10.50.0 + '@sentry/node-core': 10.50.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/opentelemetry': 10.50.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) import-in-the-middle: 3.0.1 transitivePeerDependencies: - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': + '@sentry/opentelemetry@10.50.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.7.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@sentry/core': 10.47.0 + '@sentry/core': 10.50.0 - '@shikijs/core@4.0.2': + '@shikijs/core@4.2.0': dependencies: - '@shikijs/primitive': 4.0.2 - '@shikijs/types': 4.0.2 + '@shikijs/primitive': 4.2.0 + '@shikijs/types': 4.2.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@4.0.2': + '@shikijs/engine-javascript@4.2.0': dependencies: - '@shikijs/types': 4.0.2 + '@shikijs/types': 4.2.0 '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.4 + oniguruma-to-es: 4.3.6 - '@shikijs/engine-oniguruma@4.0.2': + '@shikijs/engine-oniguruma@4.2.0': dependencies: - '@shikijs/types': 4.0.2 + '@shikijs/types': 4.2.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@4.0.2': + '@shikijs/langs@4.2.0': dependencies: - '@shikijs/types': 4.0.2 + '@shikijs/types': 4.2.0 - '@shikijs/primitive@4.0.2': + '@shikijs/primitive@4.2.0': dependencies: - '@shikijs/types': 4.0.2 + '@shikijs/types': 4.2.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 - '@shikijs/themes@4.0.2': + '@shikijs/themes@4.2.0': dependencies: - '@shikijs/types': 4.0.2 + '@shikijs/types': 4.2.0 - '@shikijs/types@4.0.2': + '@shikijs/types@4.2.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -11358,105 +11218,105 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tailwindcss/node@4.2.4': + '@tailwindcss/node@4.3.0': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.20.1 - jiti: 2.6.1 + enhanced-resolve: 5.21.2 + jiti: 2.7.0 lightningcss: 1.32.0 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.2.4 + tailwindcss: 4.3.0 - '@tailwindcss/oxide-android-arm64@4.2.4': + '@tailwindcss/oxide-android-arm64@4.3.0': optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.4': + '@tailwindcss/oxide-darwin-arm64@4.3.0': optional: true - '@tailwindcss/oxide-darwin-x64@4.2.4': + '@tailwindcss/oxide-darwin-x64@4.3.0': optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.4': + '@tailwindcss/oxide-freebsd-x64@4.3.0': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.4': + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.4': + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.4': + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.4': + '@tailwindcss/oxide-linux-x64-musl@4.3.0': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.4': + '@tailwindcss/oxide-wasm32-wasi@4.3.0': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.4': + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.4': + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': optional: true - '@tailwindcss/oxide@4.2.4': + '@tailwindcss/oxide@4.3.0': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.2.4 - '@tailwindcss/oxide-darwin-arm64': 4.2.4 - '@tailwindcss/oxide-darwin-x64': 4.2.4 - '@tailwindcss/oxide-freebsd-x64': 4.2.4 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.4 - '@tailwindcss/oxide-linux-arm64-gnu': 4.2.4 - '@tailwindcss/oxide-linux-arm64-musl': 4.2.4 - '@tailwindcss/oxide-linux-x64-gnu': 4.2.4 - '@tailwindcss/oxide-linux-x64-musl': 4.2.4 - '@tailwindcss/oxide-wasm32-wasi': 4.2.4 - '@tailwindcss/oxide-win32-arm64-msvc': 4.2.4 - '@tailwindcss/oxide-win32-x64-msvc': 4.2.4 - - '@tailwindcss/postcss@4.2.4': + '@tailwindcss/oxide-android-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-x64': 4.3.0 + '@tailwindcss/oxide-freebsd-x64': 4.3.0 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.0 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-x64-musl': 4.3.0 + '@tailwindcss/oxide-wasm32-wasi': 4.3.0 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 + + '@tailwindcss/postcss@4.3.0': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.2.4 - '@tailwindcss/oxide': 4.2.4 - postcss: 8.5.10 - tailwindcss: 4.2.4 + '@tailwindcss/node': 4.3.0 + '@tailwindcss/oxide': 4.3.0 + postcss: 8.5.15 + tailwindcss: 4.3.0 - '@tanstack/query-core@5.100.1': {} + '@tanstack/query-core@5.101.0': {} - '@tanstack/react-query@5.100.1(react@19.2.5)': + '@tanstack/react-query@5.101.0(react@19.2.7)': dependencies: - '@tanstack/query-core': 5.100.1 - react: 19.2.5 + '@tanstack/query-core': 5.101.0 + react: 19.2.7 '@tootallnate/once@2.0.1': {} - '@turbo/darwin-64@2.9.14': + '@turbo/darwin-64@2.9.16': optional: true - '@turbo/darwin-arm64@2.9.14': + '@turbo/darwin-arm64@2.9.16': optional: true - '@turbo/linux-64@2.9.14': + '@turbo/linux-64@2.9.16': optional: true - '@turbo/linux-arm64@2.9.14': + '@turbo/linux-arm64@2.9.16': optional: true - '@turbo/windows-64@2.9.14': + '@turbo/windows-64@2.9.16': optional: true - '@turbo/windows-arm64@2.9.14': + '@turbo/windows-arm64@2.9.16': optional: true '@tweenjs/tween.js@25.0.0': {} - '@tybys/wasm-util@0.10.1': + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 optional: true @@ -11469,7 +11329,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.2.0 '@types/keyv': 3.1.4 - '@types/node': 22.19.3 + '@types/node': 25.9.2 '@types/responselike': 1.0.3 '@types/chai@5.2.3': @@ -11479,7 +11339,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 '@types/debug@4.1.13': dependencies: @@ -11487,17 +11347,17 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/esrecurse@4.3.1': {} + '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.8 - - '@types/estree@1.0.8': {} + '@types/estree': 1.0.9 '@types/estree@1.0.9': {} '@types/fs-extra@9.0.13': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 '@types/hast@3.0.4': dependencies: @@ -11509,39 +11369,31 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 - '@types/mdx@2.0.13': {} + '@types/mdx@2.0.14': {} '@types/ms@2.1.0': {} '@types/mysql@2.15.27': dependencies: - '@types/node': 22.19.3 - - '@types/node@20.19.27': - dependencies: - undici-types: 6.21.0 + '@types/node': 25.9.2 '@types/node@22.19.3': dependencies: undici-types: 6.21.0 - '@types/node@24.12.2': - dependencies: - undici-types: 7.16.0 - - '@types/node@25.4.0': + '@types/node@24.13.1': dependencies: undici-types: 7.18.2 - '@types/node@25.6.0': + '@types/node@25.9.2': dependencies: - undici-types: 7.19.2 + undici-types: 7.24.6 '@types/normalize-package-data@2.4.4': {} @@ -11551,38 +11403,32 @@ snapshots: '@types/pg@8.11.6': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 pg-protocol: 1.14.0 pg-types: 4.1.0 optional: true '@types/pg@8.15.6': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 pg-protocol: 1.13.0 pg-types: 2.2.0 - '@types/plist@3.0.5': + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: - '@types/node': 22.19.3 - xmlbuilder: 15.1.1 - optional: true + '@types/react': 19.2.17 - '@types/react-dom@19.2.3(@types/react@19.2.14)': - dependencies: - '@types/react': 19.2.14 - - '@types/react@19.2.14': + '@types/react@19.2.17': dependencies: csstype: 3.2.3 '@types/responselike@1.0.3': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 '@types/tedious@4.0.14': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 '@types/turndown@5.0.6': {} @@ -11590,109 +11436,106 @@ snapshots: '@types/unist@3.0.3': {} - '@types/verror@1.10.11': - optional: true - '@types/ws@8.18.1': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.19.3 + '@types/node': 25.9.2 optional: true - '@typescript-eslint/eslint-plugin@8.59.0(@typescript-eslint/parser@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/type-utils': 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.59.0 - eslint: 9.39.2(jiti@2.6.1) + '@typescript-eslint/parser': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.60.1 + '@typescript-eslint/type-utils': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.60.1 + eslint: 10.4.1(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.59.0 + '@typescript-eslint/scope-manager': 8.60.1 + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.60.1 debug: 4.4.3 - eslint: 9.39.2(jiti@2.6.1) - typescript: 5.9.3 + eslint: 10.4.1(jiti@2.7.0) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.60.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3) - '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.3) + '@typescript-eslint/types': 8.60.1 debug: 4.4.3 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.59.0': + '@typescript-eslint/scope-manager@8.60.1': dependencies: - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/visitor-keys': 8.59.0 + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/visitor-keys': 8.60.1 - '@typescript-eslint/tsconfig-utils@8.59.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.60.1(typescript@6.0.3)': dependencies: - typescript: 5.9.3 + typescript: 6.0.3 - '@typescript-eslint/type-utils@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) debug: 4.4.3 - eslint: 9.39.2(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + eslint: 10.4.1(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.51.0': {} + '@typescript-eslint/types@8.59.2': {} - '@typescript-eslint/types@8.59.0': {} + '@typescript-eslint/types@8.60.1': {} - '@typescript-eslint/typescript-estree@8.59.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.60.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.59.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3) - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/visitor-keys': 8.59.0 + '@typescript-eslint/project-service': 8.60.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.3) + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/visitor-keys': 8.60.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.2 tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.3) - eslint: 9.39.2(jiti@2.6.1) - typescript: 5.9.3 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.60.1 + '@typescript-eslint/types': 8.60.1 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) + eslint: 10.4.1(jiti@2.7.0) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.59.0': + '@typescript-eslint/visitor-keys@8.60.1': dependencies: - '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/types': 8.60.1 eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.0': {} @@ -11756,107 +11599,63 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react@6.0.1(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - '@vitest/expect@3.2.6': + '@vitest/coverage-v8@4.1.8(vitest@4.1.8)': dependencies: - '@types/chai': 5.2.3 - '@vitest/spy': 3.2.6 - '@vitest/utils': 3.2.6 - chai: 5.3.3 - tinyrainbow: 2.0.0 + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.8 + ast-v8-to-istanbul: 1.0.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.2 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/expect@4.1.0': + '@vitest/expect@4.1.8': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@3.2.6(vite@7.3.5(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 3.2.6 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.5(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - - '@vitest/mocker@4.1.0(vite@8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 4.1.0 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - - '@vitest/mocker@4.1.0(vite@8.0.10(@types/node@22.19.3)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 4.1.0 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.0.10(@types/node@22.19.3)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - - '@vitest/mocker@4.1.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.0 + '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - - '@vitest/pretty-format@3.2.6': - dependencies: - tinyrainbow: 2.0.0 + vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - '@vitest/pretty-format@4.1.0': + '@vitest/pretty-format@4.1.8': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@3.2.6': - dependencies: - '@vitest/utils': 3.2.6 - pathe: 2.0.3 - strip-literal: 3.1.0 - - '@vitest/runner@4.1.0': - dependencies: - '@vitest/utils': 4.1.0 - pathe: 2.0.3 - - '@vitest/snapshot@3.2.6': + '@vitest/runner@4.1.8': dependencies: - '@vitest/pretty-format': 3.2.6 - magic-string: 0.30.21 + '@vitest/utils': 4.1.8 pathe: 2.0.3 - '@vitest/snapshot@4.1.0': + '@vitest/snapshot@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/pretty-format': 4.1.8 + '@vitest/utils': 4.1.8 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@3.2.6': - dependencies: - tinyspy: 4.0.4 - - '@vitest/spy@4.1.0': {} - - '@vitest/utils@3.2.6': - dependencies: - '@vitest/pretty-format': 3.2.6 - loupe: 3.2.1 - tinyrainbow: 2.0.0 + '@vitest/spy@4.1.8': {} - '@vitest/utils@4.1.0': + '@vitest/utils@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.0 + '@vitest/pretty-format': 4.1.8 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -11881,16 +11680,10 @@ snapshots: dependencies: acorn: 8.16.0 - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 - acorn@8.15.0: {} - acorn@8.16.0: {} agent-base@6.0.2: @@ -11901,6 +11694,8 @@ snapshots: agent-base@7.1.4: {} + agent-base@9.0.0: {} + agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 @@ -11923,13 +11718,6 @@ snapshots: dependencies: ajv: 6.15.0 - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -11944,6 +11732,13 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + algoliasearch@5.46.2: dependencies: '@algolia/abtesting': 1.12.2 @@ -11984,7 +11779,7 @@ snapshots: app-builder-bin@5.0.0-alpha.12: {} - app-builder-lib@26.0.12(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.0.12): + app-builder-lib@26.0.12(dmg-builder@26.15.2)(electron-builder-squirrel-windows@26.0.12): dependencies: '@develar/schema-utils': 2.6.5 '@electron/asar': 3.2.18 @@ -12001,11 +11796,11 @@ snapshots: chromium-pickle-js: 0.2.0 config-file-ts: 0.2.8-rc1 debug: 4.4.3 - dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.0.12) + dmg-builder: 26.15.2(electron-builder-squirrel-windows@26.0.12) dotenv: 16.6.1 dotenv-expand: 11.0.7 ejs: 3.1.10 - electron-builder-squirrel-windows: 26.0.12(dmg-builder@26.8.1) + electron-builder-squirrel-windows: 26.0.12(dmg-builder@26.15.2) electron-publish: 26.0.11 fs-extra: 10.1.0 hosted-git-info: 4.1.0 @@ -12025,9 +11820,8 @@ snapshots: - bluebird - supports-color - app-builder-lib@26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.0.12): + app-builder-lib@26.15.2(dmg-builder@26.15.2)(electron-builder-squirrel-windows@26.0.12): dependencies: - '@develar/schema-utils': 2.6.5 '@electron/asar': 3.4.1 '@electron/fuses': 1.8.0 '@electron/get': 3.1.0 @@ -12036,34 +11830,40 @@ snapshots: '@electron/rebuild': 4.0.4 '@electron/universal': 2.0.3 '@malept/flatpak-bundler': 0.4.0 + '@noble/hashes': 2.2.0 + '@peculiar/webcrypto': 1.7.1 '@types/fs-extra': 9.0.13 + ajv: 8.20.0 + asn1js: 3.0.10 async-exit-hook: 2.0.1 - builder-util: 26.8.1 - builder-util-runtime: 9.5.1 + builder-util: 26.15.0 + builder-util-runtime: 9.7.0 chromium-pickle-js: 0.2.0 ci-info: 4.3.1 debug: 4.4.3 - dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.0.12) + dmg-builder: 26.15.2(electron-builder-squirrel-windows@26.0.12) dotenv: 16.6.1 dotenv-expand: 11.0.7 ejs: 3.1.10 - electron-builder-squirrel-windows: 26.0.12(dmg-builder@26.8.1) - electron-publish: 26.8.1 + electron-builder-squirrel-windows: 26.0.12(dmg-builder@26.15.2) + electron-publish: 26.15.1 fs-extra: 10.1.0 hosted-git-info: 4.1.0 isbinaryfile: 5.0.7 - jiti: 2.6.1 - js-yaml: 4.1.1 + jiti: 2.7.0 + js-yaml: 4.2.0 json5: 2.2.3 lazy-val: 1.0.5 minimatch: 10.2.5 + pkijs: 3.4.0 plist: 3.1.0 proper-lockfile: 4.1.2 resedit: 1.7.2 semver: 7.7.4 - tar: 7.5.13 + tar: 7.5.16 temp-file: 3.4.0 tiny-async-pool: 1.3.0 + unzipper: 0.12.3 which: 5.0.0 transitivePeerDependencies: - supports-color @@ -12078,13 +11878,19 @@ snapshots: array-ify@1.0.0: {} - assert-plus@1.0.0: - optional: true + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 assertion-error@2.0.1: {} - astral-regex@2.0.0: - optional: true + ast-v8-to-istanbul@1.0.3: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 astring@1.9.0: {} @@ -12104,6 +11910,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + aws4@1.13.2: {} + bail@2.0.2: {} balanced-match@1.0.2: {} @@ -12112,16 +11920,11 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.29: {} + baseline-browser-mapping@2.10.34: {} before-after-hook@4.0.0: {} - better-sqlite3@11.10.0: - dependencies: - bindings: 1.5.0 - prebuild-install: 7.1.3 - - better-sqlite3@12.9.0: + better-sqlite3@12.10.0: dependencies: bindings: 1.5.0 prebuild-install: 7.1.3 @@ -12140,6 +11943,8 @@ snapshots: blake3-wasm@2.1.5: {} + bluebird@3.7.2: {} + body-parser@2.2.2: dependencies: bytes: 3.1.2 @@ -12159,21 +11964,16 @@ snapshots: bottleneck@2.19.5: {} - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@1.1.14: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.0: + brace-expansion@2.1.1: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -12183,8 +11983,8 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.29 - caniuse-lite: 1.0.30001792 + baseline-browser-mapping: 2.10.34 + caniuse-lite: 1.0.30001797 electron-to-chromium: 1.5.344 node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -12210,7 +12010,7 @@ snapshots: transitivePeerDependencies: - supports-color - builder-util-runtime@9.5.1: + builder-util-runtime@9.7.0: dependencies: debug: 4.4.3 sax: 1.6.0 @@ -12239,19 +12039,17 @@ snapshots: transitivePeerDependencies: - supports-color - builder-util@26.8.1: + builder-util@26.15.0: dependencies: - 7zip-bin: 5.2.0 '@types/debug': 4.1.13 - app-builder-bin: 5.0.0-alpha.12 - builder-util-runtime: 9.5.1 + builder-util-runtime: 9.7.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 fs-extra: 10.1.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - js-yaml: 4.1.1 + js-yaml: 4.2.0 sanitize-filename: 1.6.4 source-map-support: 0.5.21 stat-mode: 1.0.0 @@ -12262,6 +12060,8 @@ snapshots: bytes@3.1.2: {} + bytestreamjs@2.0.1: {} + cac@6.7.14: {} cacache@16.1.3: @@ -12318,7 +12118,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001792: {} + caniuse-lite@1.0.30001797: {} canvas-color-tracker@1.3.2: dependencies: @@ -12326,14 +12126,6 @@ snapshots: ccount@2.0.1: {} - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.3 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 - chai@6.2.2: {} chalk@2.4.2: @@ -12359,8 +12151,6 @@ snapshots: character-reference-invalid@2.0.1: {} - check-error@2.1.3: {} - chokidar@5.0.0: dependencies: readdirp: 5.0.0 @@ -12418,16 +12208,10 @@ snapshots: optionalDependencies: '@colors/colors': 1.5.0 - cli-truncate@2.1.0: - dependencies: - slice-ansi: 3.0.0 - string-width: 4.2.3 - optional: true - cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.0 + string-width: 8.2.1 client-only@0.0.1: {} @@ -12446,7 +12230,7 @@ snapshots: cliui@9.0.1: dependencies: string-width: 7.2.0 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 wrap-ansi: 9.0.2 clone-response@1.0.3: @@ -12471,22 +12255,18 @@ snapshots: color-name@1.1.4: {} - colorette@2.0.20: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 comma-separated-tokens@2.0.3: {} - commander@14.0.3: {} - commander@5.1.0: {} commander@9.5.0: optional: true - comment-parser@1.4.1: {} + comment-parser@1.4.6: {} compare-func@2.0.0: dependencies: @@ -12529,9 +12309,9 @@ snapshots: dependencies: '@simple-libs/stream-utils': 1.2.0 conventional-commits-filter: 5.0.0 - handlebars: 4.7.8 + handlebars: 4.7.9 meow: 13.2.0 - semver: 7.7.3 + semver: 7.8.2 conventional-commits-filter@5.0.0: {} @@ -12562,28 +12342,32 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig-typescript-loader@6.3.0(@types/node@25.6.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): + cosmiconfig-typescript-loader@6.3.0(@types/node@25.9.2)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3): dependencies: - '@types/node': 25.6.0 - cosmiconfig: 9.0.1(typescript@5.9.3) + '@types/node': 25.9.2 + cosmiconfig: 9.0.2(typescript@6.0.3) jiti: 2.6.1 - typescript: 5.9.3 + typescript: 6.0.3 - cosmiconfig@9.0.1(typescript@5.9.3): + cosmiconfig@9.0.1(typescript@6.0.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.2.0 parse-json: 5.2.0 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 - crc-32@1.2.2: {} - - crc@3.8.0: + cosmiconfig@9.0.2(typescript@6.0.3): dependencies: - buffer: 5.7.1 - optional: true + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.2.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 6.0.3 + + crc-32@1.2.2: {} crelt@1.0.6: {} @@ -12685,10 +12469,6 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - date-fns@4.1.0: {} - - dateformat@4.6.3: {} - debug@4.4.3: dependencies: ms: 2.1.3 @@ -12697,12 +12477,14 @@ snapshots: dependencies: character-entities: 2.0.2 + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 - deep-eql@5.0.2: {} - deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -12758,31 +12540,16 @@ snapshots: dependencies: path-type: 4.0.0 - dmg-builder@26.8.1(electron-builder-squirrel-windows@26.0.12): + dmg-builder@26.15.2(electron-builder-squirrel-windows@26.0.12): dependencies: - app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.0.12) - builder-util: 26.8.1 + app-builder-lib: 26.15.2(dmg-builder@26.15.2)(electron-builder-squirrel-windows@26.0.12) + builder-util: 26.15.0 fs-extra: 10.1.0 - iconv-lite: 0.6.3 - js-yaml: 4.1.1 - optionalDependencies: - dmg-license: 1.0.11 + js-yaml: 4.2.0 transitivePeerDependencies: - electron-builder-squirrel-windows - supports-color - dmg-license@1.0.11: - dependencies: - '@types/plist': 3.0.5 - '@types/verror': 1.10.11 - ajv: 6.15.0 - crc: 3.8.0 - iconv-corefoundation: 1.1.7 - plist: 3.1.0 - smart-buffer: 4.2.0 - verror: 1.10.1 - optional: true - dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -12800,15 +12567,15 @@ snapshots: esbuild: 0.25.12 tsx: 4.21.0 - drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260423.1)(@libsql/client@0.17.3)(@neondatabase/serverless@0.10.4)(@opentelemetry/api@1.9.1)(@types/better-sqlite3@7.6.13)(@types/pg@8.15.6)(better-sqlite3@12.9.0)(gel@2.2.0)(sql.js@1.14.1): + drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260608.1)(@libsql/client@0.17.3)(@neondatabase/serverless@0.10.4)(@opentelemetry/api@1.9.1)(@types/better-sqlite3@7.6.13)(@types/pg@8.15.6)(better-sqlite3@12.10.0)(gel@2.2.0)(sql.js@1.14.1): optionalDependencies: - '@cloudflare/workers-types': 4.20260423.1 + '@cloudflare/workers-types': 4.20260608.1 '@libsql/client': 0.17.3 '@neondatabase/serverless': 0.10.4 '@opentelemetry/api': 1.9.1 '@types/better-sqlite3': 7.6.13 '@types/pg': 8.15.6 - better-sqlite3: 12.9.0 + better-sqlite3: 12.10.0 gel: 2.2.0 sql.js: 1.14.1 @@ -12830,9 +12597,9 @@ snapshots: dependencies: jake: 10.9.4 - electron-builder-squirrel-windows@26.0.12(dmg-builder@26.8.1): + electron-builder-squirrel-windows@26.0.12(dmg-builder@26.15.2): dependencies: - app-builder-lib: 26.0.12(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.0.12) + app-builder-lib: 26.0.12(dmg-builder@26.15.2)(electron-builder-squirrel-windows@26.0.12) builder-util: 26.0.11 electron-winstaller: 5.4.0 transitivePeerDependencies: @@ -12840,14 +12607,14 @@ snapshots: - dmg-builder - supports-color - electron-builder@26.8.1(electron-builder-squirrel-windows@26.0.12): + electron-builder@26.15.2(electron-builder-squirrel-windows@26.0.12): dependencies: - app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.0.12) - builder-util: 26.8.1 - builder-util-runtime: 9.5.1 + app-builder-lib: 26.15.2(dmg-builder@26.15.2)(electron-builder-squirrel-windows@26.0.12) + builder-util: 26.15.0 + builder-util-runtime: 9.7.0 chalk: 4.1.2 ci-info: 4.4.0 - dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.0.12) + dmg-builder: 26.15.2(electron-builder-squirrel-windows@26.0.12) fs-extra: 10.1.0 lazy-val: 1.0.5 simple-update-notifier: 2.0.0 @@ -12873,11 +12640,12 @@ snapshots: transitivePeerDependencies: - supports-color - electron-publish@26.8.1: + electron-publish@26.15.1: dependencies: '@types/fs-extra': 9.0.13 - builder-util: 26.8.1 - builder-util-runtime: 9.5.1 + aws4: 1.13.2 + builder-util: 26.15.0 + builder-util-runtime: 9.7.0 chalk: 4.1.2 form-data: 4.0.5 fs-extra: 10.1.0 @@ -12888,11 +12656,11 @@ snapshots: electron-to-chromium@1.5.344: {} - electron-updater@6.8.3: + electron-updater@6.8.9: dependencies: - builder-util-runtime: 9.5.1 + builder-util-runtime: 9.7.0 fs-extra: 10.1.0 - js-yaml: 4.1.1 + js-yaml: 4.2.0 lazy-val: 1.0.5 lodash.escaperegexp: 4.1.2 lodash.isequal: 4.5.0 @@ -12901,7 +12669,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron-vite@5.0.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): + electron-vite@5.0.0(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) @@ -12909,7 +12677,7 @@ snapshots: esbuild: 0.25.12 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -12925,10 +12693,10 @@ snapshots: transitivePeerDependencies: - supports-color - electron@41.3.0: + electron@42.3.3: dependencies: - '@electron/get': 2.0.3 - '@types/node': 24.12.2 + '@electron/get': 5.0.0 + '@types/node': 24.13.1 extract-zip: 2.0.1 transitivePeerDependencies: - supports-color @@ -12952,7 +12720,7 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.20.1: + enhanced-resolve@5.21.2: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -12966,8 +12734,7 @@ snapshots: env-paths@2.2.1: {} - env-paths@3.0.0: - optional: true + env-paths@3.0.0: {} environment@1.1.0: {} @@ -12983,11 +12750,9 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} - es-module-lexer@2.1.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -12996,7 +12761,9 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.3 + hasown: 2.0.4 + + es-toolkit@1.47.0: {} es6-error@4.1.1: optional: true @@ -13139,61 +12906,59 @@ snapshots: eslint-import-context@0.1.9(unrs-resolver@1.11.1): dependencies: - get-tsconfig: 4.13.0 + get-tsconfig: 4.14.0 stable-hash-x: 0.2.0 optionalDependencies: unrs-resolver: 1.11.1 - eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0)): dependencies: - '@typescript-eslint/types': 8.51.0 - comment-parser: 1.4.1 + '@package-json/types': 0.0.12 + '@typescript-eslint/types': 8.59.2 + comment-parser: 1.4.6 debug: 4.4.3 - eslint: 9.39.2(jiti@2.6.1) + eslint: 10.4.1(jiti@2.7.0) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 - minimatch: 10.1.1 - semver: 7.7.3 + minimatch: 10.2.5 + semver: 7.8.0 stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) transitivePeerDependencies: - supports-color - eslint-scope@8.4.0: + eslint-scope@9.1.2: dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.1: {} - eslint-visitor-keys@5.0.1: {} - eslint@9.39.2(jiti@2.6.1): + eslint@10.4.1(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.2 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.6.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.2 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 + '@types/estree': 1.0.9 + ajv: 6.15.0 cross-spawn: 7.0.6 debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -13204,20 +12969,19 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 10.2.5 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.6.1 + jiti: 2.7.0 transitivePeerDependencies: - supports-color - espree@10.4.0: + espree@11.2.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.1 + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 esquery@1.7.0: dependencies: @@ -13231,7 +12995,7 @@ snapshots: estree-util-attach-comments@3.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-util-build-jsx@3.0.1: dependencies: @@ -13244,7 +13008,7 @@ snapshots: estree-util-scope@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 devlop: 1.1.0 estree-util-to-js@2.0.0: @@ -13255,7 +13019,7 @@ snapshots: estree-util-value-to-estree@3.5.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-util-visit@2.0.0: dependencies: @@ -13377,23 +13141,34 @@ snapshots: transitivePeerDependencies: - supports-color - extsprintf@1.4.1: - optional: true - fast-content-type-parse@3.0.0: {} - fast-copy@4.0.2: {} - fast-deep-equal@3.1.3: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fast-safe-stringify@2.1.1: {} - fast-uri@3.1.0: {} + fast-uri@3.1.2: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fd-package-json@2.0.0: + dependencies: + walk-up-path: 4.0.0 + fd-slicer@1.1.0: dependencies: pend: 1.2.0 @@ -13453,10 +13228,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.3 + flatted: 3.4.2 keyv: 4.5.4 - flatted@3.3.3: {} + flatted@3.4.2: {} float-tooltip@1.7.5: dependencies: @@ -13496,29 +13271,28 @@ snapshots: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.3 + hasown: 2.0.4 mime-types: 2.1.35 + formatly@0.3.0: + dependencies: + fd-package-json: 2.0.0 + forwarded-parse@2.1.2: {} forwarded@0.2.0: {} - framer-motion@12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + framer-motion@12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - motion-dom: 12.38.0 - motion-utils: 12.36.0 + motion-dom: 12.40.0 + motion-utils: 12.39.0 tslib: 2.8.1 optionalDependencies: - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) fresh@2.0.0: {} - from2@2.3.0: - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.8 - fs-constants@1.0.0: {} fs-extra@10.1.0: @@ -13564,25 +13338,28 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true - fumadocs-core@16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6): + fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3): dependencies: '@orama/orama': 3.1.18 estree-util-value-to-estree: 3.5.0 github-slugger: 2.0.0 hast-util-to-estree: 3.1.3 hast-util-to-jsx-runtime: 2.3.6 - js-yaml: 4.1.1 + js-yaml: 4.2.0 mdast-util-mdx: 3.0.0 mdast-util-to-markdown: 2.1.2 remark: 15.0.1 remark-gfm: 4.0.1 remark-rehype: 11.1.2 scroll-into-view-if-needed: 3.1.0 - shiki: 4.0.2 - tinyglobby: 0.2.16 + shiki: 4.2.0 + tinyglobby: 0.2.17 unified: 11.0.5 unist-util-visit: 5.1.0 vfile: 6.0.3 @@ -13591,76 +13368,76 @@ snapshots: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.2.14 + '@types/react': 19.2.17 algoliasearch: 5.46.2 - lucide-react: 1.8.0(react@19.2.5) - next: 16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - zod: 4.3.6 + lucide-react: 1.17.0(react@19.2.7) + next: 16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + zod: 4.4.3 transitivePeerDependencies: - supports-color - fumadocs-mdx@14.3.1(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)(vite@8.0.10(@types/node@25.4.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): + fumadocs-mdx@15.0.11(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(rolldown@1.0.3)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 chokidar: 5.0.0 esbuild: 0.28.0 estree-util-value-to-estree: 3.5.0 - fumadocs-core: 16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6) - js-yaml: 4.1.1 + fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) + js-yaml: 4.2.0 mdast-util-mdx: 3.0.0 - mdast-util-to-markdown: 2.1.2 picocolors: 1.1.1 picomatch: 4.0.4 - tinyexec: 1.1.1 - tinyglobby: 0.2.16 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 unified: 11.0.5 unist-util-remove-position: 5.0.0 unist-util-visit: 5.1.0 vfile: 6.0.3 - zod: 4.3.6 + zod: 4.4.3 optionalDependencies: '@types/mdast': 4.0.4 - '@types/mdx': 2.0.13 - '@types/react': 19.2.14 - next: 16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - vite: 8.0.10(@types/node@25.4.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + '@types/mdx': 2.0.14 + '@types/react': 19.2.17 + next: 16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + rolldown: 1.0.3 + vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: - supports-color - fumadocs-ui@16.8.2(@tailwindcss/oxide@4.2.4)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4): - dependencies: - '@fumadocs/tailwind': 0.0.5(@tailwindcss/oxide@4.2.4)(tailwindcss@4.2.4) - '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + fumadocs-ui@16.9.3(@tailwindcss/oxide@4.3.0)(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0): + dependencies: + '@fumadocs/tailwind': 0.0.5(@tailwindcss/oxide@4.3.0)(tailwindcss@4.3.0) + '@radix-ui/react-accordion': 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collapsible': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-navigation-menu': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popover': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-scroll-area': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-tabs': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) class-variance-authority: 0.7.1 - fumadocs-core: 16.8.2(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(algoliasearch@5.46.2)(lucide-react@1.8.0(react@19.2.5))(next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6) - lucide-react: 1.8.0(react@19.2.5) - motion: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - next-themes: 0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5) + fumadocs-core: 16.9.3(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.46.2)(lucide-react@1.17.0(react@19.2.7))(next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@4.4.3) + lucide-react: 1.17.0(react@19.2.7) + motion: 12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next-themes: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) rehype-raw: 7.0.0 scroll-into-view-if-needed: 3.1.0 - shiki: 4.0.2 - tailwind-merge: 3.5.0 + shiki: 4.2.0 + tailwind-merge: 3.6.0 unist-util-visit: 5.1.0 optionalDependencies: - '@types/mdx': 2.0.13 - '@types/react': 19.2.14 - next: 16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@types/mdx': 2.0.14 + '@types/react': 19.2.17 + next: 16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) transitivePeerDependencies: - '@emotion/is-prop-valid' - '@tailwindcss/oxide' @@ -13687,21 +13464,19 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.4.0: {} - - get-east-asian-width@1.5.0: {} + get-east-asian-width@1.6.0: {} get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.3 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-nonce@1.0.1: {} @@ -13709,7 +13484,7 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-stream@5.2.0: dependencies: @@ -13717,8 +13492,6 @@ snapshots: get-stream@6.0.1: {} - get-stream@7.0.1: {} - get-stream@8.0.1: {} get-stream@9.0.1: @@ -13755,6 +13528,10 @@ snapshots: github-slugger@2.0.0: {} + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -13791,15 +13568,13 @@ snapshots: es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 - semver: 7.7.4 + semver: 7.8.2 serialize-error: 7.0.1 optional: true - global-directory@4.0.1: + global-directory@5.0.0: dependencies: - ini: 4.1.1 - - globals@14.0.0: {} + ini: 6.0.0 globalthis@1.0.4: dependencies: @@ -13827,7 +13602,7 @@ snapshots: graceful-fs@4.2.11: {} - handlebars@4.7.8: + handlebars@4.7.9: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -13850,7 +13625,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.3: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -13891,7 +13666,7 @@ snapshots: hast-util-to-estree@3.1.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 @@ -13902,7 +13677,7 @@ snapshots: mdast-util-mdx-expression: 2.0.1 mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 - property-information: 7.1.0 + property-information: 7.2.0 space-separated-tokens: 2.0.2 style-to-js: 1.1.21 unist-util-position: 5.0.0 @@ -13919,14 +13694,14 @@ snapshots: hast-util-whitespace: 3.0.0 html-void-elements: 3.0.0 mdast-util-to-hast: 13.2.1 - property-information: 7.1.0 + property-information: 7.2.0 space-separated-tokens: 2.0.2 stringify-entities: 4.0.4 zwitch: 2.0.4 hast-util-to-jsx-runtime@2.3.6: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/hast': 3.0.4 '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 @@ -13970,11 +13745,9 @@ snapshots: '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 - property-information: 7.1.0 + property-information: 7.2.0 space-separated-tokens: 2.0.2 - help-me@5.0.0: {} - highlight.js@10.7.3: {} highlight.js@11.11.1: {} @@ -13995,6 +13768,8 @@ snapshots: dependencies: lru-cache: 11.2.6 + html-escaper@2.0.2: {} + html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} @@ -14024,6 +13799,13 @@ snapshots: transitivePeerDependencies: - supports-color + http-proxy-agent@9.0.0: + dependencies: + agent-base: 9.0.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + http2-wrapper@1.0.3: dependencies: quick-lru: 5.1.1 @@ -14043,6 +13825,13 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@9.0.0: + dependencies: + agent-base: 9.0.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + human-signals@2.1.0: {} human-signals@5.0.0: {} @@ -14053,17 +13842,10 @@ snapshots: dependencies: ms: 2.1.3 - husky@9.1.7: {} - - iconv-corefoundation@1.1.7: - dependencies: - cli-truncate: 2.1.0 - node-addon-api: 1.7.2 - optional: true - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + optional: true iconv-lite@0.7.2: dependencies: @@ -14126,17 +13908,12 @@ snapshots: ini@1.3.8: {} - ini@4.1.1: {} + ini@6.0.0: {} inline-style-parser@0.2.7: {} internmap@2.0.3: {} - into-stream@7.0.0: - dependencies: - from2: 2.3.0 - p-is-promise: 3.0.0 - ip-address@10.1.0: {} ip-address@10.2.0: {} @@ -14166,7 +13943,7 @@ snapshots: is-fullwidth-code-point@5.1.0: dependencies: - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 is-glob@4.0.3: dependencies: @@ -14194,7 +13971,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 is-unicode-supported@0.1.0: {} @@ -14214,7 +13991,7 @@ snapshots: isexe@4.0.0: {} - isomorphic-git@1.37.5: + isomorphic-git@1.38.4: dependencies: async-lock: 1.4.1 clean-git-ref: 2.0.1 @@ -14228,7 +14005,7 @@ snapshots: sha.js: 2.4.12 simple-get: 4.0.1 - issue-parser@7.0.1: + issue-parser@7.0.2: dependencies: lodash.capitalize: 4.2.1 lodash.escaperegexp: 4.1.2 @@ -14236,6 +14013,19 @@ snapshots: lodash.isstring: 4.0.1 lodash.uniqby: 4.7.0 + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -14254,19 +14044,15 @@ snapshots: jiti@2.6.1: {} - jose@6.2.2: {} + jiti@2.7.0: {} - joycon@3.1.1: {} + jose@6.2.3: {} js-base64@3.7.8: {} - js-tokens@4.0.0: {} - - js-tokens@9.0.1: {} + js-tokens@10.0.0: {} - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 + js-tokens@4.0.0: {} js-yaml@4.2.0: dependencies: @@ -14291,7 +14077,7 @@ snapshots: json-stringify-safe@5.0.1: optional: true - json-with-bigint@3.5.7: {} + json-with-bigint@3.5.8: {} json5@2.2.3: {} @@ -14328,8 +14114,69 @@ snapshots: kleur@4.1.5: {} + knip@5.88.1(@types/node@25.9.2)(typescript@6.0.3): + dependencies: + '@nodelib/fs.walk': 1.2.8 + '@types/node': 25.9.2 + fast-glob: 3.3.3 + formatly: 0.3.0 + jiti: 2.7.0 + minimist: 1.2.8 + oxc-resolver: 11.20.0 + picocolors: 1.1.1 + picomatch: 4.0.4 + smol-toml: 1.6.1 + strip-json-comments: 5.0.3 + typescript: 6.0.3 + unbash: 2.2.0 + yaml: 2.9.0 + zod: 4.4.3 + lazy-val@1.0.5: {} + lefthook-darwin-arm64@1.13.6: + optional: true + + lefthook-darwin-x64@1.13.6: + optional: true + + lefthook-freebsd-arm64@1.13.6: + optional: true + + lefthook-freebsd-x64@1.13.6: + optional: true + + lefthook-linux-arm64@1.13.6: + optional: true + + lefthook-linux-x64@1.13.6: + optional: true + + lefthook-openbsd-arm64@1.13.6: + optional: true + + lefthook-openbsd-x64@1.13.6: + optional: true + + lefthook-windows-arm64@1.13.6: + optional: true + + lefthook-windows-x64@1.13.6: + optional: true + + lefthook@1.13.6: + optionalDependencies: + lefthook-darwin-arm64: 1.13.6 + lefthook-darwin-x64: 1.13.6 + lefthook-freebsd-arm64: 1.13.6 + lefthook-freebsd-x64: 1.13.6 + lefthook-linux-arm64: 1.13.6 + lefthook-linux-x64: 1.13.6 + lefthook-openbsd-arm64: 1.13.6 + lefthook-openbsd-x64: 1.13.6 + lefthook-windows-arm64: 1.13.6 + lefthook-windows-x64: 1.13.6 + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -14405,23 +14252,22 @@ snapshots: lines-and-columns@1.2.4: {} - lint-staged@16.4.0: + lint-staged@17.0.7: dependencies: - commander: 14.0.3 - listr2: 9.0.5 + listr2: 10.2.1 picomatch: 4.0.4 string-argv: 0.3.2 - tinyexec: 1.1.1 - yaml: 2.8.3 + tinyexec: 1.2.4 + optionalDependencies: + yaml: 2.9.0 - listr2@9.0.5: + listr2@10.2.1: dependencies: cli-truncate: 5.2.0 - colorette: 2.0.20 eventemitter3: 5.0.4 log-update: 6.1.0 rfdc: 1.4.1 - wrap-ansi: 9.0.2 + wrap-ansi: 10.0.0 load-json-file@4.0.0: dependencies: @@ -14443,8 +14289,6 @@ snapshots: lodash-es@4.18.1: {} - lodash.camelcase@4.3.0: {} - lodash.capitalize@4.2.1: {} lodash.escaperegexp@4.1.2: {} @@ -14455,20 +14299,8 @@ snapshots: lodash.isstring@4.0.1: {} - lodash.kebabcase@4.1.1: {} - - lodash.merge@4.6.2: {} - - lodash.mergewith@4.6.2: {} - - lodash.snakecase@4.1.1: {} - - lodash.startcase@4.4.0: {} - lodash.uniqby@4.7.0: {} - lodash.upperfirst@4.3.1: {} - lodash@4.17.23: {} lodash@4.18.1: {} @@ -14492,8 +14324,6 @@ snapshots: dependencies: js-tokens: 4.0.0 - loupe@3.2.1: {} - lowercase-keys@2.0.0: {} lowlight@3.3.0: @@ -14516,20 +14346,30 @@ snapshots: lru-cache@7.18.3: {} - lucide-react@1.8.0(react@19.2.5): + lucide-react@1.17.0(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + make-asynchronous@1.1.0: dependencies: p-event: 6.0.1 type-fest: 4.41.0 web-worker: 1.5.0 + make-dir@4.0.0: + dependencies: + semver: 7.8.2 + make-fetch-happen@10.2.1: dependencies: agentkeepalive: 4.6.0 @@ -14569,7 +14409,7 @@ snapshots: marked@15.0.12: {} - marked@18.0.2: {} + marked@18.0.5: {} matcher@3.0.0: dependencies: @@ -14585,11 +14425,28 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@2.0.2: + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-from-markdown@2.0.3: dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 micromark: 4.0.2 @@ -14689,7 +14546,7 @@ snapshots: mdast-util-mdx@3.0.0: dependencies: - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-mdx-expression: 2.0.1 mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 @@ -14749,6 +14606,8 @@ snapshots: merge-stream@2.0.0: {} + merge2@1.4.1: {} + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.2.0 @@ -14947,7 +14806,7 @@ snapshots: micromark-util-decode-string@2.0.1: dependencies: - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 micromark-util-character: 2.1.1 micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-symbol: 2.0.1 @@ -15044,41 +14903,33 @@ snapshots: mimic-response@3.1.0: {} - miniflare@4.20260421.0: + miniflare@4.20260603.0: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.24.8 - workerd: 1.20260421.1 - ws: 8.18.0 + workerd: 1.20260603.1 + ws: 8.20.1 youch: 4.1.0-beta.10 transitivePeerDependencies: - bufferutil - utf-8-validate - minimatch@10.1.1: - dependencies: - '@isaacs/brace-expansion': 5.0.0 - minimatch@10.2.5: dependencies: - brace-expansion: 5.0.5 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 + brace-expansion: 5.0.6 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 1.1.15 minimatch@5.1.9: dependencies: - brace-expansion: 2.1.0 + brace-expansion: 2.1.1 minimatch@9.0.9: dependencies: - brace-expansion: 2.1.0 + brace-expansion: 2.1.1 minimist@1.2.8: {} @@ -15137,19 +14988,19 @@ snapshots: module-details-from-path@1.0.4: {} - motion-dom@12.38.0: + motion-dom@12.40.0: dependencies: - motion-utils: 12.36.0 + motion-utils: 12.39.0 - motion-utils@12.36.0: {} + motion-utils@12.39.0: {} - motion@12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + motion@12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - framer-motion: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + framer-motion: 12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tslib: 2.8.1 optionalDependencies: - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) ms@2.1.3: {} @@ -15159,8 +15010,6 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} - nanoid@3.3.12: {} napi-build-utils@2.0.0: {} @@ -15177,54 +15026,48 @@ snapshots: nerf-dart@1.0.0: {} - next-themes@0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - next@16.2.6(@opentelemetry/api@1.9.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + next@16.2.7(@opentelemetry/api@1.9.1)(@playwright/test@1.60.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@next/env': 16.2.6 + '@next/env': 16.2.7 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.29 - caniuse-lite: 1.0.30001792 + baseline-browser-mapping: 2.10.34 + caniuse-lite: 1.0.30001797 postcss: 8.4.31 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - styled-jsx: 5.1.6(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + styled-jsx: 5.1.6(react@19.2.7) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.6 - '@next/swc-darwin-x64': 16.2.6 - '@next/swc-linux-arm64-gnu': 16.2.6 - '@next/swc-linux-arm64-musl': 16.2.6 - '@next/swc-linux-x64-gnu': 16.2.6 - '@next/swc-linux-x64-musl': 16.2.6 - '@next/swc-win32-arm64-msvc': 16.2.6 - '@next/swc-win32-x64-msvc': 16.2.6 + '@next/swc-darwin-arm64': 16.2.7 + '@next/swc-darwin-x64': 16.2.7 + '@next/swc-linux-arm64-gnu': 16.2.7 + '@next/swc-linux-arm64-musl': 16.2.7 + '@next/swc-linux-x64-gnu': 16.2.7 + '@next/swc-linux-x64-musl': 16.2.7 + '@next/swc-win32-arm64-msvc': 16.2.7 + '@next/swc-win32-x64-msvc': 16.2.7 '@opentelemetry/api': 1.9.1 + '@playwright/test': 1.60.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - node-abi@3.89.0: - dependencies: - semver: 7.7.4 - node-abi@3.92.0: dependencies: semver: 7.8.2 - node-abi@4.28.0: + node-abi@4.31.0: dependencies: - semver: 7.8.0 - - node-addon-api@1.7.2: - optional: true + semver: 7.8.2 node-api-version@0.2.1: dependencies: - semver: 7.8.0 + semver: 7.8.2 node-emoji@2.2.0: dependencies: @@ -15239,19 +15082,21 @@ snapshots: optionalDependencies: encoding: 0.1.13 - node-gyp@12.3.0: + node-gyp@12.4.0: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.3 graceful-fs: 4.2.11 nopt: 9.0.0 proc-log: 6.1.0 - semver: 7.8.0 - tar: 7.5.13 + semver: 7.8.2 + tar: 7.5.16 tinyglobby: 0.2.17 - undici: 6.25.0 + undici: 6.26.0 which: 6.0.1 + node-int64@0.4.0: {} + node-releases@2.0.38: {} nopt@6.0.0: @@ -15265,13 +15110,13 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.8.0 + semver: 7.8.2 validate-npm-package-license: 3.0.4 normalize-package-data@8.0.0: dependencies: hosted-git-info: 9.0.2 - semver: 7.8.0 + semver: 7.8.2 validate-npm-package-license: 3.0.4 normalize-url@6.1.0: {} @@ -15303,7 +15148,7 @@ snapshots: obuf@1.1.2: optional: true - obug@2.1.1: {} + obug@2.1.2: {} on-exit-leak-free@2.1.2: {} @@ -15327,11 +15172,11 @@ snapshots: dependencies: mimic-function: 5.0.1 - oniguruma-parser@0.12.1: {} + oniguruma-parser@0.12.2: {} - oniguruma-to-es@4.3.4: + oniguruma-to-es@4.3.6: dependencies: - oniguruma-parser: 0.12.1 + oniguruma-parser: 0.12.2 regex: 6.1.0 regex-recursion: 6.0.2 @@ -15356,6 +15201,28 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + oxc-resolver@11.20.0: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.20.0 + '@oxc-resolver/binding-android-arm64': 11.20.0 + '@oxc-resolver/binding-darwin-arm64': 11.20.0 + '@oxc-resolver/binding-darwin-x64': 11.20.0 + '@oxc-resolver/binding-freebsd-x64': 11.20.0 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.20.0 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.20.0 + '@oxc-resolver/binding-linux-arm64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-arm64-musl': 11.20.0 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-riscv64-musl': 11.20.0 + '@oxc-resolver/binding-linux-s390x-gnu': 11.20.0 + '@oxc-resolver/binding-linux-x64-gnu': 11.20.0 + '@oxc-resolver/binding-linux-x64-musl': 11.20.0 + '@oxc-resolver/binding-openharmony-arm64': 11.20.0 + '@oxc-resolver/binding-wasm32-wasi': 11.20.0 + '@oxc-resolver/binding-win32-arm64-msvc': 11.20.0 + '@oxc-resolver/binding-win32-x64-msvc': 11.20.0 + p-cancelable@2.1.1: {} p-each-series@3.0.0: {} @@ -15368,8 +15235,6 @@ snapshots: dependencies: p-map: 7.0.4 - p-is-promise@3.0.0: {} - p-limit@1.3.0: dependencies: p-try: 1.0.0 @@ -15475,8 +15340,6 @@ snapshots: pathe@2.0.3: {} - pathval@2.0.1: {} - pe-library@0.4.1: {} pend@1.2.0: {} @@ -15524,27 +15387,6 @@ snapshots: dependencies: split2: 4.2.0 - pino-pretty@13.1.3: - dependencies: - colorette: 2.0.20 - dateformat: 4.6.3 - fast-copy: 4.0.2 - fast-safe-stringify: 2.1.1 - help-me: 5.0.0 - joycon: 3.1.1 - minimist: 1.2.8 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pump: 3.0.3 - secure-json-parse: 4.1.0 - sonic-boom: 4.2.0 - strip-json-comments: 5.0.3 - - pino-roll@4.0.0: - dependencies: - date-fns: 4.1.0 - sonic-boom: 4.2.0 - pino-std-serializers@7.1.0: {} pino@10.3.1: @@ -15568,6 +15410,23 @@ snapshots: find-up: 2.1.0 load-json-file: 4.0.0 + pkijs@3.4.0: + dependencies: + '@noble/hashes': 1.4.0 + asn1js: 3.0.10 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + playwright-core@1.60.0: {} + + playwright@1.60.0: + dependencies: + playwright-core: 1.60.0 + optionalDependencies: + fsevents: 2.3.2 + plist@3.1.0: dependencies: '@xmldom/xmldom': 0.8.13 @@ -15582,9 +15441,9 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.10: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -15630,7 +15489,7 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.89.0 + node-abi: 3.92.0 pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 @@ -15639,7 +15498,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.7.4: {} + prettier@3.8.3: {} pretty-ms@9.3.0: dependencies: @@ -15680,6 +15539,8 @@ snapshots: property-information@7.1.0: {} + property-information@7.2.0: {} + proto-list@1.2.4: {} proxy-addr@2.0.7: @@ -15687,11 +15548,6 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -15699,10 +15555,18 @@ snapshots: punycode@2.3.1: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.5: {} + qs@6.15.1: dependencies: side-channel: 1.1.0 + queue-microtask@1.2.3: {} + quick-format-unescaped@4.0.4: {} quick-lru@5.1.1: {} @@ -15723,35 +15587,35 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-dom@19.2.5(react@19.2.5): + react-dom@19.2.7(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 scheduler: 0.27.0 - react-force-graph-2d@1.29.1(react@19.2.5): + react-force-graph-2d@1.29.1(react@19.2.7): dependencies: force-graph: 1.51.4 prop-types: 15.8.1 - react: 19.2.5 - react-kapsule: 2.5.7(react@19.2.5) + react: 19.2.7 + react-kapsule: 2.5.7(react@19.2.7) react-is@16.13.1: {} - react-kapsule@2.5.7(react@19.2.5): + react-kapsule@2.5.7(react@19.2.7): dependencies: jerrypick: 1.1.2 - react: 19.2.5 + react: 19.2.7 - react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.5): + react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.7): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.2.14 + '@types/react': 19.2.17 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.1 - react: 19.2.5 + react: 19.2.7 remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 @@ -15760,39 +15624,34 @@ snapshots: transitivePeerDependencies: - supports-color - react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.5): + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.2.5 - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.5): + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.2.5 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.5) - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.5) - use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.5) + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - - react-resizable-panels@4.10.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): - dependencies: - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@types/react': 19.2.17 - react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.5): + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): dependencies: get-nonce: 1.0.1 - react: 19.2.5 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - react@19.2.5: {} + react@19.2.7: {} read-binary-file-arch@1.0.6: dependencies: @@ -15817,7 +15676,7 @@ snapshots: '@types/normalize-package-data': 2.4.4 normalize-package-data: 8.0.0 parse-json: 8.3.0 - type-fest: 5.4.4 + type-fest: 5.6.0 unicorn-magic: 0.4.0 read-pkg@9.0.1: @@ -15858,14 +15717,14 @@ snapshots: recma-build-jsx@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-util-build-jsx: 3.0.1 vfile: 6.0.3 - recma-jsx@1.0.1(acorn@8.15.0): + recma-jsx@1.0.1(acorn@8.16.0): dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) estree-util-to-js: 2.0.0 recma-parse: 1.0.0 recma-stringify: 1.0.0 @@ -15880,7 +15739,7 @@ snapshots: recma-stringify@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-util-to-js: 2.0.0 unified: 11.0.5 vfile: 6.0.3 @@ -15915,7 +15774,7 @@ snapshots: rehype-recma@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/hast': 3.0.4 hast-util-to-estree: 3.1.3 transitivePeerDependencies: @@ -16010,6 +15869,8 @@ snapshots: retry@0.12.0: {} + reusify@1.1.0: {} + rfdc@1.4.1: {} rimraf@2.6.3: @@ -16030,57 +15891,26 @@ snapshots: sprintf-js: 1.1.3 optional: true - rolldown@1.0.0-rc.17: - dependencies: - '@oxc-project/types': 0.127.0 - '@rolldown/pluginutils': 1.0.0-rc.17 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-x64': 1.0.0-rc.17 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 - - rollup@4.61.0: + rolldown@1.0.3: dependencies: - '@types/estree': 1.0.9 + '@oxc-project/types': 0.133.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.61.0 - '@rollup/rollup-android-arm64': 4.61.0 - '@rollup/rollup-darwin-arm64': 4.61.0 - '@rollup/rollup-darwin-x64': 4.61.0 - '@rollup/rollup-freebsd-arm64': 4.61.0 - '@rollup/rollup-freebsd-x64': 4.61.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.61.0 - '@rollup/rollup-linux-arm-musleabihf': 4.61.0 - '@rollup/rollup-linux-arm64-gnu': 4.61.0 - '@rollup/rollup-linux-arm64-musl': 4.61.0 - '@rollup/rollup-linux-loong64-gnu': 4.61.0 - '@rollup/rollup-linux-loong64-musl': 4.61.0 - '@rollup/rollup-linux-ppc64-gnu': 4.61.0 - '@rollup/rollup-linux-ppc64-musl': 4.61.0 - '@rollup/rollup-linux-riscv64-gnu': 4.61.0 - '@rollup/rollup-linux-riscv64-musl': 4.61.0 - '@rollup/rollup-linux-s390x-gnu': 4.61.0 - '@rollup/rollup-linux-x64-gnu': 4.61.0 - '@rollup/rollup-linux-x64-musl': 4.61.0 - '@rollup/rollup-openbsd-x64': 4.61.0 - '@rollup/rollup-openharmony-arm64': 4.61.0 - '@rollup/rollup-win32-arm64-msvc': 4.61.0 - '@rollup/rollup-win32-ia32-msvc': 4.61.0 - '@rollup/rollup-win32-x64-gnu': 4.61.0 - '@rollup/rollup-win32-x64-msvc': 4.61.0 - fsevents: 2.3.3 + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 router@2.2.0: dependencies: @@ -16092,6 +15922,10 @@ snapshots: transitivePeerDependencies: - supports-color + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} @@ -16112,17 +15946,15 @@ snapshots: dependencies: compute-scroll-into-view: 3.1.1 - secure-json-parse@4.1.0: {} - - semantic-release@25.0.3(typescript@5.9.3): + semantic-release@25.0.3(typescript@6.0.3): dependencies: - '@semantic-release/commit-analyzer': 13.0.1(semantic-release@25.0.3(typescript@5.9.3)) + '@semantic-release/commit-analyzer': 13.0.1(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/error': 4.0.0 - '@semantic-release/github': 12.0.6(semantic-release@25.0.3(typescript@5.9.3)) - '@semantic-release/npm': 13.1.5(semantic-release@25.0.3(typescript@5.9.3)) - '@semantic-release/release-notes-generator': 14.1.0(semantic-release@25.0.3(typescript@5.9.3)) + '@semantic-release/github': 12.0.8(semantic-release@25.0.3(typescript@6.0.3)) + '@semantic-release/npm': 13.1.5(semantic-release@25.0.3(typescript@6.0.3)) + '@semantic-release/release-notes-generator': 14.1.1(semantic-release@25.0.3(typescript@6.0.3)) aggregate-error: 5.0.0 - cosmiconfig: 9.0.1(typescript@5.9.3) + cosmiconfig: 9.0.1(typescript@6.0.3) debug: 4.4.3 env-ci: 11.2.0 execa: 9.6.1 @@ -16218,7 +16050,7 @@ snapshots: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.8.0 + semver: 7.8.2 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -16254,14 +16086,14 @@ snapshots: shell-quote@1.8.4: optional: true - shiki@4.0.2: + shiki@4.2.0: dependencies: - '@shikijs/core': 4.0.2 - '@shikijs/engine-javascript': 4.0.2 - '@shikijs/engine-oniguruma': 4.0.2 - '@shikijs/langs': 4.0.2 - '@shikijs/themes': 4.0.2 - '@shikijs/types': 4.0.2 + '@shikijs/core': 4.2.0 + '@shikijs/engine-javascript': 4.2.0 + '@shikijs/engine-oniguruma': 4.2.0 + '@shikijs/langs': 4.2.0 + '@shikijs/themes': 4.2.0 + '@shikijs/types': 4.2.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -16315,19 +16147,12 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.2 skin-tone@2.0.0: dependencies: unicode-emoji-modifier-base: 1.0.0 - slice-ansi@3.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - optional: true - slice-ansi@7.1.2: dependencies: ansi-styles: 6.2.3 @@ -16340,6 +16165,8 @@ snapshots: smart-buffer@4.2.0: {} + smol-toml@1.6.1: {} + socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 @@ -16353,10 +16180,6 @@ snapshots: ip-address: 10.2.0 smart-buffer: 4.2.0 - sonic-boom@4.2.0: - dependencies: - atomic-sleep: 1.0.0 - sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -16414,8 +16237,6 @@ snapshots: statuses@2.0.2: {} - std-env@3.10.0: {} - std-env@4.1.0: {} stream-combiner2@1.1.1: @@ -16440,12 +16261,12 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 - string-width@8.2.0: + string-width@8.2.1: dependencies: - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 string_decoder@1.1.1: @@ -16465,10 +16286,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -16483,17 +16300,11 @@ snapshots: strip-json-comments@2.0.1: {} - strip-json-comments@3.1.1: {} - strip-json-comments@5.0.3: {} - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - - stripe@22.0.2(@types/node@25.6.0): + stripe@22.2.0(@types/node@25.9.2): optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.2 style-mod@4.1.3: {} @@ -16505,10 +16316,10 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - styled-jsx@5.1.6(react@19.2.5): + styled-jsx@5.1.6(react@19.2.7): dependencies: client-only: 0.0.1 - react: 19.2.5 + react: 19.2.7 sumchecker@3.0.1: dependencies: @@ -16539,13 +16350,13 @@ snapshots: tagged-tag@1.0.0: {} - tailwind-merge@3.5.0: {} + tailwind-merge@3.6.0: {} - tailwindcss-animate@1.0.7(tailwindcss@4.2.4): + tailwindcss-animate@1.0.7(tailwindcss@4.3.0): dependencies: - tailwindcss: 4.2.4 + tailwindcss: 4.3.0 - tailwindcss@4.2.4: {} + tailwindcss@4.3.0: {} tapable@2.3.3: {} @@ -16573,7 +16384,7 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - tar@7.5.13: + tar@7.5.16: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -16631,17 +16442,8 @@ snapshots: tinycolor2@1.6.0: {} - tinyexec@0.3.2: {} - - tinyexec@1.1.1: {} - tinyexec@1.2.4: {} - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -16652,19 +16454,13 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - tinyrainbow@3.1.0: {} - tinyspy@4.0.4: {} - tmp-promise@3.0.3: dependencies: - tmp: 0.2.5 + tmp: 0.2.7 - tmp@0.2.5: {} + tmp@0.2.7: {} to-buffer@1.2.2: dependencies: @@ -16690,9 +16486,9 @@ snapshots: dependencies: utf8-byte-length: 1.0.5 - ts-api-utils@2.5.0(typescript@5.9.3): + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: - typescript: 5.9.3 + typescript: 6.0.3 tslib@2.8.1: {} @@ -16703,20 +16499,26 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.22.4: + dependencies: + esbuild: 0.28.0 + optionalDependencies: + fsevents: 2.3.3 + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 tunnel@0.0.6: {} - turbo@2.9.14: + turbo@2.9.16: optionalDependencies: - '@turbo/darwin-64': 2.9.14 - '@turbo/darwin-arm64': 2.9.14 - '@turbo/linux-64': 2.9.14 - '@turbo/linux-arm64': 2.9.14 - '@turbo/windows-64': 2.9.14 - '@turbo/windows-arm64': 2.9.14 + '@turbo/darwin-64': 2.9.16 + '@turbo/darwin-arm64': 2.9.16 + '@turbo/linux-64': 2.9.16 + '@turbo/linux-arm64': 2.9.16 + '@turbo/windows-64': 2.9.16 + '@turbo/windows-arm64': 2.9.16 turndown-plugin-gfm@1.0.2: {} @@ -16741,6 +16543,10 @@ snapshots: dependencies: tagged-tag: 1.0.0 + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -16753,36 +16559,41 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.15 - typescript-eslint@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.59.0(@typescript-eslint/parser@8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.2(jiti@2.6.1) - typescript: 5.9.3 + '@typescript-eslint/eslint-plugin': 8.60.1(@typescript-eslint/parser@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.4.1(jiti@2.7.0) + typescript: 6.0.3 transitivePeerDependencies: - supports-color typescript@5.9.3: {} + typescript@6.0.3: {} + uglify-js@3.19.3: optional: true - undici-types@6.21.0: {} + unbash@2.2.0: {} - undici-types@7.16.0: {} + undici-types@6.21.0: {} undici-types@7.18.2: {} - undici-types@7.19.2: {} - - undici@6.25.0: {} + undici-types@7.24.6: {} - undici@7.18.2: {} + undici@6.26.0: {} undici@7.24.8: {} + undici@7.25.0: {} + + undici@7.27.2: + optional: true + unenv@2.0.0-rc.24: dependencies: pathe: 2.0.3 @@ -16892,6 +16703,14 @@ snapshots: mkdirp: 0.5.6 yaku: 0.16.7 + unzipper@0.12.3: + dependencies: + bluebird: 3.7.2 + duplexer2: 0.1.4 + fs-extra: 11.3.5 + graceful-fs: 4.2.11 + node-int64: 0.4.0 + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: browserslist: 4.28.2 @@ -16904,24 +16723,24 @@ snapshots: url-join@5.0.0: {} - use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.5): + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.5): + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): dependencies: detect-node-es: 1.1.0 - react: 19.2.5 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - use-sync-external-store@1.6.0(react@19.2.5): + use-sync-external-store@1.6.0(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 optional: true utf8-byte-length@1.0.5: {} @@ -16935,13 +16754,6 @@ snapshots: vary@1.1.2: {} - verror@1.10.1: - dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.4.1 - optional: true - vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -16957,215 +16769,34 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.5(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite@7.3.5(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.10 - rollup: 4.61.0 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 22.19.3 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.32.0 - tsx: 4.21.0 - yaml: 2.8.3 - - vite@8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - lightningcss: 1.32.0 - picomatch: 4.0.4 - postcss: 8.5.10 - rolldown: 1.0.0-rc.17 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 20.19.27 - esbuild: 0.28.0 - fsevents: 2.3.3 - jiti: 2.6.1 - tsx: 4.21.0 - yaml: 2.8.3 - - vite@8.0.10(@types/node@22.19.3)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - lightningcss: 1.32.0 - picomatch: 4.0.4 - postcss: 8.5.10 - rolldown: 1.0.0-rc.17 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 22.19.3 - esbuild: 0.28.0 - fsevents: 2.3.3 - jiti: 2.6.1 - tsx: 4.21.0 - yaml: 2.8.3 - - vite@8.0.10(@types/node@25.4.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - lightningcss: 1.32.0 - picomatch: 4.0.4 - postcss: 8.5.10 - rolldown: 1.0.0-rc.17 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 25.4.0 - esbuild: 0.28.0 - fsevents: 2.3.3 - jiti: 2.6.1 - tsx: 4.21.0 - yaml: 2.8.3 - optional: true - - vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): + vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 - postcss: 8.5.10 - rolldown: 1.0.0-rc.17 - tinyglobby: 0.2.16 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.2 esbuild: 0.28.0 fsevents: 2.3.3 - jiti: 2.6.1 - tsx: 4.21.0 - yaml: 2.8.3 - - vitest@3.2.6(@types/debug@4.1.13)(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.6 - '@vitest/mocker': 3.2.6(vite@7.3.5(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 3.2.6 - '@vitest/runner': 3.2.6 - '@vitest/snapshot': 3.2.6 - '@vitest/spy': 3.2.6 - '@vitest/utils': 3.2.6 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.17 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.5(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - vite-node: 3.2.4(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/debug': 4.1.13 - '@types/node': 22.19.3 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vitest@4.1.0(@opentelemetry/api@1.9.1)(@types/node@20.19.27)(vite@8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.0.10(@types/node@20.19.27)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 20.19.27 - transitivePeerDependencies: - - msw - - vitest@4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.19.3)(vite@8.0.10(@types/node@22.19.3)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@8.0.10(@types/node@22.19.3)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.0.10(@types/node@22.19.3)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 22.19.3 - transitivePeerDependencies: - - msw - - vitest@4.1.0(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + jiti: 2.7.0 + tsx: 4.22.4 + yaml: 2.9.0 + + vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.9.2)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.8 + '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.8 + '@vitest/runner': 4.1.8 + '@vitest/snapshot': 4.1.8 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 - obug: 2.1.1 + obug: 2.1.2 pathe: 2.0.3 picomatch: 4.0.4 std-env: 4.1.0 @@ -17173,16 +16804,19 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 25.6.0 + '@types/node': 25.9.2 + '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) transitivePeerDependencies: - msw w3c-keyname@2.2.8: {} + walk-up-path@4.0.0: {} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 @@ -17191,6 +16825,14 @@ snapshots: web-worker@1.5.0: {} + webcrypto-core@1.9.2: + dependencies: + '@peculiar/asn1-schema': 2.7.0 + '@peculiar/json-schema': 1.1.12 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + webidl-conversions@3.0.1: {} whatwg-url@5.0.0: @@ -17198,7 +16840,7 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-typed-array@1.1.20: + which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 @@ -17234,31 +16876,37 @@ snapshots: wordwrap@1.0.0: {} - workerd@1.20260421.1: + workerd@1.20260603.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260421.1 - '@cloudflare/workerd-darwin-arm64': 1.20260421.1 - '@cloudflare/workerd-linux-64': 1.20260421.1 - '@cloudflare/workerd-linux-arm64': 1.20260421.1 - '@cloudflare/workerd-windows-64': 1.20260421.1 + '@cloudflare/workerd-darwin-64': 1.20260603.1 + '@cloudflare/workerd-darwin-arm64': 1.20260603.1 + '@cloudflare/workerd-linux-64': 1.20260603.1 + '@cloudflare/workerd-linux-arm64': 1.20260603.1 + '@cloudflare/workerd-windows-64': 1.20260603.1 - wrangler@4.84.1(@cloudflare/workers-types@4.20260423.1): + wrangler@4.98.0(@cloudflare/workers-types@4.20260608.1): dependencies: - '@cloudflare/kv-asset-handler': 0.4.2 - '@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260421.1) + '@cloudflare/kv-asset-handler': 0.5.0 + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260603.1) blake3-wasm: 2.1.5 esbuild: 0.27.3 - miniflare: 4.20260421.0 + miniflare: 4.20260603.0 path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260421.1 + workerd: 1.20260603.1 optionalDependencies: - '@cloudflare/workers-types': 4.20260423.1 + '@cloudflare/workers-types': 4.20260608.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil - utf-8-validate + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.1 + strip-ansi: 7.2.0 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -17279,10 +16927,10 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.0: {} - ws@8.20.0: {} + ws@8.20.1: {} + xmlbuilder@15.1.1: {} xtend@4.0.2: {} @@ -17297,7 +16945,7 @@ snapshots: yallist@5.0.0: {} - yaml@2.8.3: {} + yaml@2.9.0: {} yargs-parser@20.2.9: {} @@ -17356,16 +17004,16 @@ snapshots: cookie: 1.1.1 youch-core: 0.3.3 - zod-to-json-schema@3.25.2(zod@4.3.6): + zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: - zod: 4.3.6 + zod: 4.4.3 - zod@4.3.6: {} + zod@4.4.3: {} - zustand@5.0.12(@types/react@19.2.14)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)): + zustand@5.0.14(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)): optionalDependencies: - '@types/react': 19.2.14 - react: 19.2.5 - use-sync-external-store: 1.6.0(react@19.2.5) + '@types/react': 19.2.17 + react: 19.2.7 + use-sync-external-store: 1.6.0(react@19.2.7) zwitch@2.0.4: {} diff --git a/scripts/bump-version.js b/scripts/bump-version.js deleted file mode 100644 index 7dcb54d1..00000000 --- a/scripts/bump-version.js +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env node -import { readFileSync, writeFileSync } from 'fs'; -import { resolve } from 'path'; - -const version = process.argv[2]; -if (!version) { - console.error('Usage: bump-version.js '); - process.exit(1); -} - -if ( - !/^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test( - version - ) -) { - console.error(`Invalid version format: ${version}`); - process.exit(1); -} - -const files = ['package.json', 'apps/desktop/package.json']; - -for (const file of files) { - const path = resolve(file); - try { - const pkg = JSON.parse(readFileSync(path, 'utf8')); - pkg.version = version; - writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n'); - console.log(`Updated ${file} -> ${version}`); - } catch (err) { - console.error(`Failed to update ${file}: ${err.message}`); - process.exit(1); - } -} diff --git a/tsconfig.base.json b/tsconfig.base.json index 9a354640..740dec6d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -18,6 +18,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true + "noUncheckedIndexedAccess": true, + "ignoreDeprecations": "6.0" } } diff --git a/vitest.shared.ts b/vitest.shared.ts new file mode 100644 index 00000000..48b3e069 --- /dev/null +++ b/vitest.shared.ts @@ -0,0 +1,23 @@ +/** + * Shared vitest configuration fragments. + * + * Each package's vitest.config.ts can spread these to opt into consistent + * coverage reporting. Thresholds are NOT enforced yet — this is baseline + * measurement only. When per-package floors are known, replace `undefined` + * with `{ lines: N, functions: N, branches: N }` in that package's config. + */ +import type { UserConfig } from 'vitest/config'; + +export const sharedCoverage: NonNullable['coverage']> = { + provider: 'v8', + reporter: ['text', 'lcov'], + include: ['src/**/*.{ts,tsx}'], + exclude: [ + '**/*.test.{ts,tsx}', + '**/__tests__/**', + '**/tests/**', + '**/dist/**', + '**/*.d.ts', + '**/index.{ts,tsx}', + ], +};