From 6da0d1c3c275c920af00a90a9345ec211ad30226 Mon Sep 17 00:00:00 2001 From: "F." Date: Sun, 14 Jun 2026 01:25:41 +0200 Subject: [PATCH 1/6] feat(web): buffer and download browser session as replay JSONL Hosted and browser-dev runtimes record telemetry and link activity in memory, export replay-compatible JSONL locally, warn before navigation when buffered, and clear on reset without any server upload. Co-authored-by: Cursor --- README.md | 4 +- apps/desktop/package.json | 2 +- apps/desktop/src-tauri/Cargo.toml | 2 +- apps/desktop/src-tauri/tauri.conf.json | 2 +- apps/web/src/App.tsx | 6 + apps/web/src/components/Topbar.tsx | 21 +++ apps/web/src/hooks/useTelemetry.ts | 126 ++++++++++++++++-- apps/web/src/hooks/useTelemetrySource.ts | 10 ++ apps/web/src/lib/sessionRecorder.test.ts | 34 +++++ apps/web/src/lib/sessionRecorder.ts | 157 +++++++++++++++++++++++ package.json | 2 +- 11 files changed, 350 insertions(+), 16 deletions(-) create mode 100644 apps/web/src/lib/sessionRecorder.test.ts create mode 100644 apps/web/src/lib/sessionRecorder.ts diff --git a/README.md b/README.md index d9aa9fc..dfbbf60 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The **Hosted Web App** (internal runtime key `cloud`) is a pure browser SPA that | Static SPA over HTTPS | Accounts or login | | Web Serial CRSF + MAVLink link (user-granted port only) | Server-side telemetry persistence | | Replay and simulation (frontend-only, no upload) | Fleet or project management | -| Same dashboard UI as desktop/browser dev | Cloud logging or sync | +| **Download session** — buffer live telemetry in memory; export replay JSONL locally (no upload) | Cloud logging or sync | | | Billing or paid tiers | Self-host the Hosted Web App with `pnpm build:cloud` and serve **`apps/web/dist` from that command only** — not the output of `pnpm build` (desktop/Node stack). A wrong artifact shows the COM port dropdown and spams `WebSocket connection to …/ws failed` because the app is in `web` mode instead of `cloud`. Do not embed the app in a cross-origin iframe without a `Permissions-Policy` that allows `serial` for your origin — Web Serial is blocked by default in embedded contexts. @@ -61,7 +61,7 @@ Prefer bench testing and disconnected telemetry validation before using the soft - **Optional video stream** (MJPEG, etc.) via environment variables; crosshair overlay; when the stream is live, a **Ground Target** panel docks beside the camera feed - **Ground target estimation** (desktop) — image-center target with GeoTIFF DEM ray marching, map marker, line-of-sight, and sample-log export (shown next to the camera when video is live) - **Preflight health advisory** — sensor and link health checks with configurable thresholds -- **Session logging** and reset for new flights +- **Session logging** — desktop and browser dev write JSONL to disk via **Start Log**; the **Hosted Web App** and browser dev also buffer telemetry in memory and export with **Download session** (replay-compatible JSONL, never uploaded). **Reset** clears the in-memory buffer. - **Onboarding tour** — first-run walkthrough of link controls, telemetry sidebar, map, camera, and activity log; skip anytime; restart from the **?** button in the top bar (`localStorage` keys `uav-gcs.onboarding.*`) - **Replay & Simulation** — frontend-only, read-only telemetry sources that drive the same dashboard without hardware: replay recorded `.jsonl`/`.json` logs (start/pause/seek/step, speed and timing modes) or run deterministic seeded simulations. See [`docs/replay-mode.md`](docs/replay-mode.md) and [`docs/adr/0003-frontend-only-replay-simulation.md`](docs/adr/0003-frontend-only-replay-simulation.md) - **Flight Review** — post-flight analysis over a loaded replay log (not simulation): summary stats, findings, colored path, seekable timeline with markers, and five click-to-seek graphs. Open manually from replay controls while in **Replay** mode; shares the replay clock with dashboard scrubbing. Frontend-only — nothing is uploaded. See [`docs/adr/0007-flight-review-replay-analysis-view.md`](docs/adr/0007-flight-review-replay-analysis-view.md) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 4ac04d9..cd672cb 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@uav-ground-control-station/desktop", - "version": "0.2.43", + "version": "0.2.44", "private": true, "license": "GPL-3.0-only", "type": "module", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 5280fb2..c27b12f 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uav-ground-control-station" -version = "0.2.43" +version = "0.2.44" description = "Native desktop shell for UAV Ground Control Station" authors = ["F. Eber"] license = "GPL-3.0-only" diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 6854490..57af514 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "UAV Ground Control Station", - "version": "0.2.43", + "version": "0.2.44", "identifier": "com.uav.ground-control-station", "build": { "beforeDevCommand": "pnpm --filter @uav-ground-control-station/web dev", diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 0d2f3e7..e28654a 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -38,12 +38,15 @@ export function App() { linkConnection, wsConnected, runtimeMode, + browserSessionExportEnabled, + sessionSnapshot, refreshPorts, connect, disconnect, resetSession, startLogging, stopLogging, + downloadSession, clearLogs, activeSourceMode, setSourceMode, @@ -170,6 +173,9 @@ export function App() { onReset={resetAll} onStartLogging={startLogging} onStopLogging={stopLogging} + onDownloadSession={downloadSession} + sessionExportEnabled={browserSessionExportEnabled} + sessionEventCount={sessionSnapshot.eventCount} onRestartTour={() => setTourRestartToken((token) => token + 1)} linkIssues={linkIssues} /> diff --git a/apps/web/src/components/Topbar.tsx b/apps/web/src/components/Topbar.tsx index ac5a75e..73c7719 100644 --- a/apps/web/src/components/Topbar.tsx +++ b/apps/web/src/components/Topbar.tsx @@ -34,6 +34,9 @@ interface TopbarProps { onReset: () => Promise; onStartLogging: () => Promise; onStopLogging: () => Promise; + onDownloadSession?: () => void; + sessionEventCount?: number; + sessionExportEnabled?: boolean; onRestartTour: () => void; linkIssues?: LinkIssue[]; } @@ -59,6 +62,9 @@ export function Topbar({ onReset, onStartLogging, onStopLogging, + onDownloadSession, + sessionEventCount = 0, + sessionExportEnabled = false, onRestartTour, linkIssues = [] }: TopbarProps) { @@ -227,6 +233,21 @@ export function Topbar({ Reset + {sessionExportEnabled && ( + + )} + {!isCloud && (loggingStatus.active ? ( + + )} + + {logReplayHandoffError && ( +
+ {logReplayHandoffError} +
+ )} ); } diff --git a/apps/web/src/hooks/useTelemetry.ts b/apps/web/src/hooks/useTelemetry.ts index 820aadc..b1caa8f 100644 --- a/apps/web/src/hooks/useTelemetry.ts +++ b/apps/web/src/hooks/useTelemetry.ts @@ -21,6 +21,11 @@ import { } from "../lib/linkErrors"; import { runtimeMode } from "../lib/runtimeMode"; import { downloadJsonlSession, SessionRecorder, type SessionRecorderSnapshot } from "../lib/sessionRecorder"; +import { + fileNameFromLogPath, + jsonlByteLength, + type StoppedLogPayload +} from "../lib/logReplayHandoff"; import { WebSerialLink } from "../link/webSerialLink"; const apiBase = import.meta.env.VITE_API_BASE_URL ?? ""; @@ -158,6 +163,31 @@ export function useTelemetry() { addLog("success", "Session downloaded as replay JSONL."); }, [addLog]); + const readStoppedSessionLog = useCallback(async (): Promise => { + if (!loggingStatus.active && loggingStatus.filePath) { + const filePath = loggingStatus.filePath; + const fileName = fileNameFromLogPath(filePath); + if (mode === "desktop") { + const text = await invokeTauri("read_session_log", { path: filePath }); + return { text, fileName, source: "disk" }; + } + const payload = await requestJson<{ text: string; fileName: string }>("/api/logging/read"); + return { ...payload, source: "disk" }; + } + + const recorder = sessionRecorderRef.current; + if (!recorder?.hasBufferedEvents()) { + throw new Error("No session log is available for replay."); + } + + const text = recorder.toJsonlText(); + return { + text, + fileName: recorder.suggestedFileName(), + source: "buffer" + }; + }, [loggingStatus.active, loggingStatus.filePath, mode]); + const clearLogs = useCallback(() => { setLogs([]); }, []); @@ -613,6 +643,7 @@ export function useTelemetry() { stopLogging, clearLogs, downloadSession, + readStoppedSessionLog, linkConnection }), [ @@ -634,6 +665,7 @@ export function useTelemetry() { stopLogging, clearLogs, downloadSession, + readStoppedSessionLog, linkConnection ] ); diff --git a/apps/web/src/hooks/useTelemetrySource.ts b/apps/web/src/hooks/useTelemetrySource.ts index c6dc675..248b8c8 100644 --- a/apps/web/src/hooks/useTelemetrySource.ts +++ b/apps/web/src/hooks/useTelemetrySource.ts @@ -1,7 +1,9 @@ import { useCallback, useMemo, useState } from "react"; import type { ConnectRequest, TelemetryState, TelemetrySourceMode } from "@uav-ground-control-station/shared"; import type { LinkConnection } from "../lib/linkErrors"; +import { canOfferLogReplayHandoff, jsonlByteLength } from "../lib/logReplayHandoff"; import type { SessionRecorderSnapshot } from "../lib/sessionRecorder"; +import { parseReplayLog, ReplayParseError } from "../replay/parser"; import { useTelemetry, type ActivityLogEntry } from "./useTelemetry"; import { useReplayController, type ReplayController } from "./useReplayController"; @@ -40,6 +42,9 @@ export interface TelemetrySource { stopLogging: () => Promise; clearLogs: () => void; downloadSession: () => void; + canOpenLogInReplay: boolean; + logReplayHandoffError: string | null; + openStoppedLogInReplay: () => Promise; // Source-mode surface. activeSourceMode: TelemetrySourceMode; @@ -55,6 +60,7 @@ export function useTelemetrySource(): TelemetrySource { const live = useTelemetry(); const replay = useReplayController(); const [activeSourceMode, setActiveSourceMode] = useState("live"); + const [logReplayHandoffError, setLogReplayHandoffError] = useState(null); const liveControlsLocked = activeSourceMode !== "live"; @@ -117,6 +123,42 @@ export function useTelemetrySource(): TelemetrySource { replay.clearReplaySimLogs(); }, [live, replay]); + const canOpenLogInReplay = useMemo( + () => + canOfferLogReplayHandoff({ + runtimeMode: live.runtimeMode, + loggingActive: live.loggingStatus.active, + loggingFilePath: live.loggingStatus.filePath, + sessionEventCount: live.sessionSnapshot.eventCount, + liveControlsLocked + }), + [ + live.runtimeMode, + live.loggingStatus.active, + live.loggingStatus.filePath, + live.sessionSnapshot.eventCount, + liveControlsLocked + ] + ); + + const openStoppedLogInReplay = useCallback(async () => { + setLogReplayHandoffError(null); + try { + const payload = await live.readStoppedSessionLog(); + const result = parseReplayLog(payload.text, payload.fileName, jsonlByteLength(payload.text)); + setActiveSourceMode("replay"); + replay.loadParsedLog(result); + } catch (cause: unknown) { + const message = + cause instanceof ReplayParseError + ? cause.message + : cause instanceof Error + ? cause.message + : "Unable to open the session log in replay mode."; + setLogReplayHandoffError(message); + } + }, [live, replay]); + return useMemo( () => ({ telemetry, @@ -138,6 +180,9 @@ export function useTelemetrySource(): TelemetrySource { stopLogging, clearLogs, downloadSession: live.downloadSession, + canOpenLogInReplay, + logReplayHandoffError, + openStoppedLogInReplay, activeSourceMode, setSourceMode, liveControlsLocked, @@ -164,6 +209,9 @@ export function useTelemetrySource(): TelemetrySource { startLogging, stopLogging, clearLogs, + canOpenLogInReplay, + logReplayHandoffError, + openStoppedLogInReplay, activeSourceMode, setSourceMode, liveControlsLocked, diff --git a/apps/web/src/lib/logReplayHandoff.test.ts b/apps/web/src/lib/logReplayHandoff.test.ts new file mode 100644 index 0000000..8679ca4 --- /dev/null +++ b/apps/web/src/lib/logReplayHandoff.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it } from "vitest"; +import { canOfferLogReplayHandoff, fileNameFromLogPath, jsonlByteLength } from "./logReplayHandoff"; + +describe("canOfferLogReplayHandoff", () => { + it("offers cloud handoff when the session buffer has events", () => { + expect( + canOfferLogReplayHandoff({ + runtimeMode: "cloud", + loggingActive: false, + loggingFilePath: null, + sessionEventCount: 12, + liveControlsLocked: false + }) + ).toBe(true); + }); + + it("offers desktop handoff after disk logging stops", () => { + expect( + canOfferLogReplayHandoff({ + runtimeMode: "desktop", + loggingActive: false, + loggingFilePath: "/tmp/flight-2026.jsonl", + sessionEventCount: 0, + liveControlsLocked: false + }) + ).toBe(true); + }); + + it("hides handoff while replay controls the dashboard", () => { + expect( + canOfferLogReplayHandoff({ + runtimeMode: "desktop", + loggingActive: false, + loggingFilePath: "/tmp/flight-2026.jsonl", + sessionEventCount: 0, + liveControlsLocked: true + }) + ).toBe(false); + }); +}); + +describe("fileNameFromLogPath", () => { + it("uses the final path segment", () => { + expect(fileNameFromLogPath("/var/logs/flight-2026.jsonl")).toBe("flight-2026.jsonl"); + expect(fileNameFromLogPath("C:\\logs\\flight-2026.jsonl")).toBe("flight-2026.jsonl"); + }); +}); + +describe("jsonlByteLength", () => { + it("matches Blob byte length", () => { + const text = '{"type":"telemetry"}\n'; + expect(jsonlByteLength(text)).toBe(new Blob([text]).size); + }); +}); diff --git a/apps/web/src/lib/logReplayHandoff.ts b/apps/web/src/lib/logReplayHandoff.ts new file mode 100644 index 0000000..ee9e88e --- /dev/null +++ b/apps/web/src/lib/logReplayHandoff.ts @@ -0,0 +1,42 @@ +export type StoppedLogSourceKind = "disk" | "buffer"; + +export interface StoppedLogPayload { + text: string; + fileName: string; + source: StoppedLogSourceKind; +} + +export function canOfferLogReplayHandoff(input: { + runtimeMode: "web" | "desktop" | "cloud"; + loggingActive: boolean; + loggingFilePath: string | null; + sessionEventCount: number; + liveControlsLocked: boolean; +}): boolean { + if (input.liveControlsLocked) { + return false; + } + + if (input.runtimeMode === "cloud") { + return input.sessionEventCount > 0; + } + + if (!input.loggingActive && input.loggingFilePath) { + return true; + } + + if (input.runtimeMode === "web" && input.sessionEventCount > 0) { + return true; + } + + return false; +} + +export function fileNameFromLogPath(path: string): string { + const parts = path.split(/[/\\]/); + return parts[parts.length - 1] || "session.jsonl"; +} + +export function jsonlByteLength(text: string): number { + return new Blob([text]).size; +} diff --git a/package.json b/package.json index eea52ea..680a626 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uav-ground-control-station", - "version": "0.2.44", + "version": "0.2.45", "private": true, "description": "Local browser-based MAVLink ground control station for UAVs.", "license": "GPL-3.0-only", From 09c634328f5fd4335503a1f7df9fddc661e09fe4 Mon Sep 17 00:00:00 2001 From: "F." Date: Fri, 19 Jun 2026 14:55:44 +0200 Subject: [PATCH 4/6] feat(desktop): expose read log file for replay handoff Keep the stopped JSONL path after Stop Log and add a scoped read_session_log command so the desktop runtime can load disk logs into replay mode. Co-authored-by: Cursor --- apps/desktop/src-tauri/src/lib.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 07c98f5..13ed6ab 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -408,11 +408,35 @@ fn stop_logging(state: State) -> Result { writer.flush().map_err(|error| error.to_string())?; } logging.writer = None; - logging.file_path = None; Ok(logging_status_from_logger(&logging)) } +#[tauri::command] +fn read_session_log(path: String, app: AppHandle) -> Result { + let logs_dir = app + .path() + .app_log_dir() + .unwrap_or_else(|_| PathBuf::from("logs")); + let candidate = PathBuf::from(path.trim()); + let canonical = candidate + .canonicalize() + .map_err(|_| "Session log file not found.".to_string())?; + let logs_canonical = logs_dir + .canonicalize() + .unwrap_or_else(|_| logs_dir.clone()); + if !canonical.starts_with(&logs_canonical) { + return Err("Session log path is not allowed.".to_string()); + } + let lower = canonical + .to_string_lossy() + .to_ascii_lowercase(); + if !lower.ends_with(".jsonl") { + return Err("Session log must be a .jsonl file.".to_string()); + } + std::fs::read_to_string(canonical).map_err(|_| "Unable to read session log.".to_string()) +} + #[tauri::command] fn load_terrain_model(path: String, state: State) -> Result { let mut dem = state.dem.lock().map_err(lock_error)?; @@ -514,6 +538,7 @@ pub fn run() { get_telemetry, start_logging, stop_logging, + read_session_log, logging_status, load_terrain_model, get_terrain_metadata, From 15f4bf972acec3bc055ebdc9edba3fe65fbefba9 Mon Sep 17 00:00:00 2001 From: "F." Date: Fri, 19 Jun 2026 15:17:01 +0200 Subject: [PATCH 5/6] fix(web): remove unused jsonlByteLength import in useTelemetry Co-authored-by: Cursor --- apps/web/src/hooks/useTelemetry.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/web/src/hooks/useTelemetry.ts b/apps/web/src/hooks/useTelemetry.ts index b1caa8f..b28a0a9 100644 --- a/apps/web/src/hooks/useTelemetry.ts +++ b/apps/web/src/hooks/useTelemetry.ts @@ -21,11 +21,7 @@ import { } from "../lib/linkErrors"; import { runtimeMode } from "../lib/runtimeMode"; import { downloadJsonlSession, SessionRecorder, type SessionRecorderSnapshot } from "../lib/sessionRecorder"; -import { - fileNameFromLogPath, - jsonlByteLength, - type StoppedLogPayload -} from "../lib/logReplayHandoff"; +import { fileNameFromLogPath, type StoppedLogPayload } from "../lib/logReplayHandoff"; import { WebSerialLink } from "../link/webSerialLink"; const apiBase = import.meta.env.VITE_API_BASE_URL ?? ""; From 684150a4e11e16974783135486c5fa284d1f53e3 Mon Sep 17 00:00:00 2001 From: "F." Date: Fri, 19 Jun 2026 15:20:24 +0200 Subject: [PATCH 6/6] fix(deps): override vite and ws to patched audit versions Pin transitive vite (>=8.0.16) and ws (>=8.21.0) via root pnpm overrides to clear high-severity audit findings in CI. Co-authored-by: Cursor --- package.json | 4 +- pnpm-lock.yaml | 213 ++++++++++++++++++++++++++----------------------- 2 files changed, 115 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index 680a626..07b17b9 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,9 @@ "pnpm": { "overrides": { "esbuild": "^0.28.1", - "xml2js": "^0.5.0" + "xml2js": "^0.5.0", + "vite": ">=8.0.16", + "ws": ">=8.21.0" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 575a860..1db0f8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,8 @@ settings: overrides: esbuild: ^0.28.1 xml2js: ^0.5.0 + vite: '>=8.0.16' + ws: '>=8.21.0' importers: @@ -72,7 +74,7 @@ importers: version: 4.22.3 vitest: specifier: latest - version: 4.1.8(@types/node@25.9.1)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) + version: 4.1.8(@types/node@25.9.1)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) apps/web: dependencies: @@ -90,7 +92,7 @@ importers: version: link:../../packages/target-estimation '@vitejs/plugin-react': specifier: latest - version: 6.0.2(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) + version: 6.0.2(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) autoprefixer: specifier: latest version: 10.5.0(postcss@8.5.15) @@ -113,8 +115,8 @@ importers: specifier: latest version: 4.3.0 vite: - specifier: latest - version: 8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) + specifier: '>=8.0.16' + version: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) devDependencies: '@tailwindcss/postcss': specifier: latest @@ -130,7 +132,7 @@ importers: version: 19.2.3(@types/react@19.2.15) vitest: specifier: latest - version: 4.1.8(@types/node@25.9.3)(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) + version: 4.1.8(@types/node@25.9.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) packages/shared: devDependencies: @@ -139,7 +141,7 @@ importers: version: 25.9.3 vitest: specifier: latest - version: 4.1.8(@types/node@25.9.3)(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) + version: 4.1.8(@types/node@25.9.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) packages/target-estimation: dependencies: @@ -149,7 +151,7 @@ importers: devDependencies: vitest: specifier: latest - version: 4.1.8(@types/node@25.9.3)(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) + version: 4.1.8(@types/node@25.9.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) packages: @@ -557,103 +559,103 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@oxc-project/types@0.130.0': - resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - '@rolldown/binding-android-arm64@1.0.1': - resolution: {integrity: sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==} + '@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.1': - resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==} + '@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.1': - resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==} + '@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.1': - resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==} + '@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.1': - resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==} + '@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.1': - resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==} + '@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] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.1': - resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==} + '@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] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.1': - resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==} + '@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] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.1': - resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==} + '@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] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.1': - resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==} + '@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] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.1': - resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==} + '@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] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.1': - resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==} + '@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.1': - resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==} + '@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.1': - resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==} + '@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.1': - resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==} + '@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] @@ -1017,7 +1019,7 @@ packages: peerDependencies: '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 babel-plugin-react-compiler: ^1.0.0 - vite: ^8.0.0 + vite: '>=8.0.16' peerDependenciesMeta: '@rolldown/plugin-babel': optional: true @@ -1031,7 +1033,7 @@ packages: resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: '>=8.0.16' peerDependenciesMeta: msw: optional: true @@ -1839,8 +1841,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown@1.0.1: - resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==} + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -1964,6 +1966,10 @@ packages: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + tinyqueue@3.0.0: resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} @@ -2046,8 +2052,8 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - vite@8.0.13: - resolution: {integrity: sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==} + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2105,7 +2111,7 @@ packages: '@vitest/ui': 4.1.8 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: '>=8.0.16' peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -2156,8 +2162,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.20.1: - resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2476,7 +2482,7 @@ snapshots: dependencies: duplexify: 4.1.3 fastify-plugin: 5.1.0 - ws: 8.20.1 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -2589,57 +2595,57 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@oxc-project/types@0.130.0': {} + '@oxc-project/types@0.133.0': {} '@pinojs/redact@0.4.0': {} - '@rolldown/binding-android-arm64@1.0.1': + '@rolldown/binding-android-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-arm64@1.0.1': + '@rolldown/binding-darwin-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-x64@1.0.1': + '@rolldown/binding-darwin-x64@1.0.3': optional: true - '@rolldown/binding-freebsd-x64@1.0.1': + '@rolldown/binding-freebsd-x64@1.0.3': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.1': + '@rolldown/binding-linux-arm64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.1': + '@rolldown/binding-linux-arm64-musl@1.0.3': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.1': + '@rolldown/binding-linux-ppc64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.1': + '@rolldown/binding-linux-s390x-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.1': + '@rolldown/binding-linux-x64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-musl@1.0.1': + '@rolldown/binding-linux-x64-musl@1.0.3': optional: true - '@rolldown/binding-openharmony-arm64@1.0.1': + '@rolldown/binding-openharmony-arm64@1.0.3': optional: true - '@rolldown/binding-wasm32-wasi@1.0.1': + '@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.1': + '@rolldown/binding-win32-arm64-msvc@1.0.3': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.1': + '@rolldown/binding-win32-x64-msvc@1.0.3': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -2961,10 +2967,10 @@ snapshots: '@typescript-eslint/types': 8.60.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@6.0.2(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3))': + '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) + vite: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) '@vitest/expect@4.1.8': dependencies: @@ -2975,21 +2981,21 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3))': + '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) + vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) - '@vitest/mocker@4.1.8(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3))': + '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) + vite: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) '@vitest/pretty-format@4.1.8': dependencies: @@ -3805,26 +3811,26 @@ snapshots: rfdc@1.4.1: {} - rolldown@1.0.1: + rolldown@1.0.3: dependencies: - '@oxc-project/types': 0.130.0 + '@oxc-project/types': 0.133.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.1 - '@rolldown/binding-darwin-arm64': 1.0.1 - '@rolldown/binding-darwin-x64': 1.0.1 - '@rolldown/binding-freebsd-x64': 1.0.1 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.1 - '@rolldown/binding-linux-arm64-gnu': 1.0.1 - '@rolldown/binding-linux-arm64-musl': 1.0.1 - '@rolldown/binding-linux-ppc64-gnu': 1.0.1 - '@rolldown/binding-linux-s390x-gnu': 1.0.1 - '@rolldown/binding-linux-x64-gnu': 1.0.1 - '@rolldown/binding-linux-x64-musl': 1.0.1 - '@rolldown/binding-openharmony-arm64': 1.0.1 - '@rolldown/binding-wasm32-wasi': 1.0.1 - '@rolldown/binding-win32-arm64-msvc': 1.0.1 - '@rolldown/binding-win32-x64-msvc': 1.0.1 + '@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 rxjs@7.8.2: dependencies: @@ -3932,6 +3938,11 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + tinyqueue@3.0.0: {} tinyrainbow@3.1.0: {} @@ -4023,13 +4034,13 @@ snapshots: v8-compile-cache-lib@3.0.1: {} - vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3): + vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rolldown: 1.0.1 - tinyglobby: 0.2.16 + rolldown: 1.0.3 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 25.9.1 esbuild: 0.28.1 @@ -4037,13 +4048,13 @@ snapshots: jiti: 2.7.0 tsx: 4.22.3 - vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3): + vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rolldown: 1.0.1 - tinyglobby: 0.2.16 + rolldown: 1.0.3 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 25.9.3 esbuild: 0.28.1 @@ -4051,10 +4062,10 @@ snapshots: jiti: 2.7.0 tsx: 4.22.3 - vitest@4.1.8(@types/node@25.9.1)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)): + vitest@4.1.8(@types/node@25.9.1)(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) + '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -4071,17 +4082,17 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) + vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.9.1 transitivePeerDependencies: - msw - vitest@4.1.8(@types/node@25.9.3)(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)): + vitest@4.1.8(@types/node@25.9.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) + '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -4098,7 +4109,7 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.13(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) + vite: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.9.3 @@ -4135,7 +4146,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.20.1: {} + ws@8.21.0: {} xml2js@0.5.0: dependencies: