From 1b1708e1ebbdf8da19ed2d2abbc8c4a115e4a8ff Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Mon, 23 Mar 2026 18:51:42 -0700 Subject: [PATCH 01/10] refactor: extract rrweb recording code into @browser-tester/recorder package Moves all rrweb-related code (recording runtime, replay viewer, live view server, session loading) from @browser-tester/browser into a new @browser-tester/recorder package. Browser re-exports everything for backwards compatibility. The recorder runtime is re-exported from browser's runtime/index.ts so esbuild bundles it into the single __browserTesterRuntime IIFE. Co-Authored-By: Claude Opus 4.6 --- packages/browser/package.json | 4 +- packages/browser/scripts/build-runtime.js | 7 +- packages/browser/src/constants.ts | 4 -- packages/browser/src/errors.ts | 17 ----- packages/browser/src/index.ts | 14 ++-- packages/browser/src/mcp/index.ts | 2 +- packages/browser/src/mcp/mcp-session.ts | 21 +++--- packages/browser/src/runtime/index.ts | 39 ++--------- packages/browser/src/types.ts | 6 -- packages/recorder/package.json | 43 ++++++++++++ packages/recorder/src/constants.ts | 4 ++ packages/recorder/src/errors.ts | 18 +++++ packages/recorder/src/index.ts | 14 ++++ .../mcp => recorder/src}/live-view-server.ts | 8 +-- .../{browser => recorder}/src/recorder.ts | 8 +-- .../src/replay-viewer.ts | 2 +- packages/recorder/src/runtime/index.ts | 32 +++++++++ packages/recorder/src/types.ts | 6 ++ .../recorder/src/utils/evaluate-runtime.ts | 29 ++++++++ .../src/mcp => recorder/src}/viewer-events.ts | 0 .../tests/live-view-server.test.ts | 2 +- .../tests/recorder.test.ts | 0 packages/recorder/tsconfig.json | 9 +++ packages/recorder/vite.config.ts | 10 +++ pnpm-lock.yaml | 69 +++++++++++++------ 25 files changed, 259 insertions(+), 109 deletions(-) create mode 100644 packages/recorder/package.json create mode 100644 packages/recorder/src/constants.ts create mode 100644 packages/recorder/src/errors.ts create mode 100644 packages/recorder/src/index.ts rename packages/{browser/src/mcp => recorder/src}/live-view-server.ts (96%) rename packages/{browser => recorder}/src/recorder.ts (87%) rename packages/{browser => recorder}/src/replay-viewer.ts (99%) create mode 100644 packages/recorder/src/runtime/index.ts create mode 100644 packages/recorder/src/types.ts create mode 100644 packages/recorder/src/utils/evaluate-runtime.ts rename packages/{browser/src/mcp => recorder/src}/viewer-events.ts (100%) rename packages/{browser => recorder}/tests/live-view-server.test.ts (99%) rename packages/{browser => recorder}/tests/recorder.test.ts (100%) create mode 100644 packages/recorder/tsconfig.json create mode 100644 packages/recorder/vite.config.ts diff --git a/packages/browser/package.json b/packages/browser/package.json index 1664737e..323e7d98 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -32,19 +32,19 @@ "lint": "vp lint && tsc --noEmit", "format": "vp fmt", "format:check": "vp fmt --check", - "check": "vp check", + "check": "pnpm build:runtime && vp check", "test": "vp test run", "test:watch": "vp test", "typecheck": "tsgo --noEmit" }, "dependencies": { + "@browser-tester/recorder": "workspace:*", "@effect/platform-node": "4.0.0-beta.28", "@expect/cookies": "workspace:*", "@medv/finder": "^4.0.2", "@modelcontextprotocol/sdk": "^1.27.1", "effect": "4.0.0-beta.28", "playwright": "^1.52.0", - "rrweb": "^2.0.0-alpha.18", "zod": "^4.3.6" }, "devDependencies": { diff --git a/packages/browser/scripts/build-runtime.js b/packages/browser/scripts/build-runtime.js index cbb8ca6b..e4c33cc3 100644 --- a/packages/browser/scripts/build-runtime.js +++ b/packages/browser/scripts/build-runtime.js @@ -1,4 +1,5 @@ import { context } from "esbuild"; +import { execSync } from "node:child_process"; import * as fs from "node:fs"; const watchMode = process.argv.includes("--watch"); @@ -37,12 +38,16 @@ const emitPlugin = { fs.mkdirSync("src/generated", { recursive: true }); fs.writeFileSync( "src/generated/runtime-script.ts", - `export const RUNTIME_SCRIPT = ${JSON.stringify(runtimeCode)};\n`, + `export const RUNTIME_SCRIPT =\n ${JSON.stringify(runtimeCode)};\n`, ); const source = fs.readFileSync(RUNTIME_ENTRY, "utf-8"); const exportNames = extractExportedFunctionNames(source); fs.writeFileSync("src/generated/runtime-types.ts", generateRuntimeTypes(exportNames)); + + execSync("npx vp fmt src/generated/runtime-script.ts src/generated/runtime-types.ts", { + stdio: "ignore", + }); }); }, }; diff --git a/packages/browser/src/constants.ts b/packages/browser/src/constants.ts index ab3ba0ae..bc9b4ee1 100644 --- a/packages/browser/src/constants.ts +++ b/packages/browser/src/constants.ts @@ -48,7 +48,3 @@ export const ESTIMATED_CHARS_PER_TOKEN = 4; export const MAX_ELEMENT_TEXT_LENGTH = 100; export const MAX_CURSOR_INTERACTIVE_ELEMENTS = 100; export const OVERLAY_CONTAINER_ID = "__expect_annotation_overlay__"; -export const EVENT_COLLECT_INTERVAL_MS = 500; - -export const REPLAY_PLAYER_WIDTH_PX = 960; -export const REPLAY_PLAYER_HEIGHT_PX = 540; diff --git a/packages/browser/src/errors.ts b/packages/browser/src/errors.ts index 30a8aa51..57e06063 100644 --- a/packages/browser/src/errors.ts +++ b/packages/browser/src/errors.ts @@ -82,23 +82,6 @@ export class NavigationError extends Schema.ErrorClass("Navigat message = `Navigation to "${this.url}" failed: ${this.cause}`; } -export class RecorderInjectionError extends Schema.ErrorClass( - "RecorderInjectionError", -)({ - _tag: Schema.tag("RecorderInjectionError"), - cause: Schema.String, -}) { - message = `Failed to inject rrweb recorder: ${this.cause}`; -} - -export class SessionLoadError extends Schema.ErrorClass("SessionLoadError")({ - _tag: Schema.tag("SessionLoadError"), - path: Schema.String, - cause: Schema.String, -}) { - message = `Failed to load session from ${this.path}: ${this.cause}`; -} - export type ActionError = | RefAmbiguousError | RefBlockedError diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 3a241fc7..68dc9f02 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -1,7 +1,5 @@ export { Browser, runBrowser } from "./browser"; -export { buildReplayViewerHtml } from "./replay-viewer"; export { diffSnapshots } from "./diff"; -export { collectEvents, collectAllEvents, loadSession } from "./recorder"; export type { Browser as BrowserProfile, BrowserKey, @@ -13,12 +11,10 @@ export { ActionUnknownError, BrowserLaunchError, NavigationError, - RecorderInjectionError, RefAmbiguousError, RefBlockedError, RefNotFoundError, RefNotVisibleError, - SessionLoadError, SnapshotTimeoutError, } from "./errors"; export type { ActionError } from "./errors"; @@ -27,7 +23,6 @@ export type { AnnotatedScreenshotOptions, AnnotatedScreenshotResult, AriaRole, - CollectResult, CreatePageOptions, RefEntry, RefMap, @@ -36,3 +31,12 @@ export type { SnapshotResult, SnapshotStats, } from "./types"; +export { + buildReplayViewerHtml, + collectEvents, + collectAllEvents, + loadSession, + RecorderInjectionError, + SessionLoadError, +} from "@browser-tester/recorder"; +export type { CollectResult } from "@browser-tester/recorder"; diff --git a/packages/browser/src/mcp/index.ts b/packages/browser/src/mcp/index.ts index 04759aec..34c4b74a 100644 --- a/packages/browser/src/mcp/index.ts +++ b/packages/browser/src/mcp/index.ts @@ -2,4 +2,4 @@ export { EXPECT_LIVE_VIEW_URL_ENV_NAME, EXPECT_REPLAY_OUTPUT_ENV_NAME } from "./ export { McpSession } from "./mcp-session"; export { McpRuntime } from "./runtime"; export { createBrowserMcpServer, startBrowserMcpServer } from "./server"; -export type { ViewerRunState, ViewerStepEvent } from "./viewer-events"; +export type { ViewerRunState, ViewerStepEvent } from "@browser-tester/recorder"; diff --git a/packages/browser/src/mcp/mcp-session.ts b/packages/browser/src/mcp/mcp-session.ts index f9d34386..09640b41 100644 --- a/packages/browser/src/mcp/mcp-session.ts +++ b/packages/browser/src/mcp/mcp-session.ts @@ -1,19 +1,22 @@ import { basename, dirname, extname, join } from "node:path"; import type { Browser as PlaywrightBrowser, BrowserContext, Page } from "playwright"; -import type { eventWithTime } from "@rrweb/types"; import { Config, Effect, Fiber, Layer, Option, Ref, Schedule, ServiceMap } from "effect"; import { FileSystem } from "effect/FileSystem"; +import { + collectAllEvents, + evaluateRecorderRuntime, + buildReplayViewerHtml, + startLiveViewServer, + EVENT_COLLECT_INTERVAL_MS, + type eventWithTime, + type LiveViewHandle, + type ViewerRunState, +} from "@browser-tester/recorder"; import { Browser } from "../browser"; import { NavigationError } from "../errors"; -import { collectAllEvents } from "../recorder"; -import { evaluateRuntime } from "../utils/evaluate-runtime"; -import { EVENT_COLLECT_INTERVAL_MS } from "../constants"; -import { buildReplayViewerHtml } from "../replay-viewer"; import type { AnnotatedScreenshotOptions, SnapshotOptions, SnapshotResult } from "../types"; import { EXPECT_LIVE_VIEW_URL_ENV_NAME, EXPECT_REPLAY_OUTPUT_ENV_NAME } from "./constants"; import { McpSessionNotOpenError } from "./errors"; -import { startLiveViewServer, type LiveViewHandle } from "./live-view-server"; -import type { ViewerRunState } from "./viewer-events"; interface ConsoleEntry { readonly type: string; @@ -154,7 +157,7 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe setupPageTracking(pageResult.page, sessionData); yield* Ref.set(sessionRef, sessionData); - yield* evaluateRuntime(pageResult.page, "startRecording").pipe( + yield* evaluateRecorderRuntime(pageResult.page, "startRecording").pipe( Effect.catchCause((cause) => Effect.logDebug("rrweb recording failed to start", { cause })), ); @@ -183,7 +186,7 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe const pollPage = Effect.sync(() => Ref.getUnsafe(sessionRef)?.page).pipe( Effect.flatMap((page) => { if (!page || page.isClosed()) return Effect.void; - return evaluateRuntime(page, "getEvents").pipe( + return evaluateRecorderRuntime(page, "getEvents").pipe( Effect.tap((events) => Effect.sync(() => { if (Array.isArray(events) && events.length > 0) { diff --git a/packages/browser/src/runtime/index.ts b/packages/browser/src/runtime/index.ts index 6d1da7ff..2dbf4c35 100644 --- a/packages/browser/src/runtime/index.ts +++ b/packages/browser/src/runtime/index.ts @@ -1,6 +1,11 @@ import { finder } from "@medv/finder"; -import { record } from "rrweb"; -import type { eventWithTime } from "@rrweb/types"; +export { + startRecording, + stopRecording, + getEvents, + getAllEvents, + getEventCount, +} from "@browser-tester/recorder/runtime"; interface OverlayItem { label: number; @@ -105,33 +110,3 @@ export const findCursorInteractiveElements = ( return results; }; - -const eventBuffer: eventWithTime[] = []; -let stopFn: (() => void) | undefined; - -export const startRecording = (): void => { - eventBuffer.length = 0; - stopFn = - record({ - emit(event) { - eventBuffer.push(event); - }, - }) ?? undefined; -}; - -export const stopRecording = (): void => { - stopFn?.(); - stopFn = undefined; -}; - -export const getEvents = (): eventWithTime[] => { - return eventBuffer.splice(0); -}; - -export const getAllEvents = (): eventWithTime[] => { - return [...eventBuffer]; -}; - -export const getEventCount = (): number => { - return eventBuffer.length; -}; diff --git a/packages/browser/src/types.ts b/packages/browser/src/types.ts index 7dd79073..580b3b16 100644 --- a/packages/browser/src/types.ts +++ b/packages/browser/src/types.ts @@ -1,4 +1,3 @@ -import type { eventWithTime } from "@rrweb/types"; import type { Effect } from "effect"; import type { Cookie } from "@expect/cookies"; import type { Locator, Page } from "playwright"; @@ -71,8 +70,3 @@ export interface SnapshotDiff { unchanged: number; changed: boolean; } - -export interface CollectResult { - readonly events: ReadonlyArray; - readonly total: number; -} diff --git a/packages/recorder/package.json b/packages/recorder/package.json new file mode 100644 index 00000000..badfe484 --- /dev/null +++ b/packages/recorder/package.json @@ -0,0 +1,43 @@ +{ + "name": "@browser-tester/recorder", + "version": "0.0.1", + "private": true, + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs" + }, + "./runtime": { + "types": "./src/runtime/index.ts", + "import": "./src/runtime/index.ts" + } + }, + "scripts": { + "build": "vp pack", + "dev": "vp pack --watch", + "lint": "vp lint && tsc --noEmit", + "format": "vp fmt", + "format:check": "vp fmt --check", + "check": "vp check", + "test": "vp test run", + "test:watch": "vp test", + "typecheck": "tsgo --noEmit" + }, + "dependencies": { + "@effect/platform-node": "4.0.0-beta.28", + "@rrweb/types": "^2.0.0-alpha.18", + "effect": "4.0.0-beta.28", + "playwright": "^1.52.0", + "rrweb": "^2.0.0-alpha.18" + }, + "devDependencies": { + "@types/node": "^22.15.0", + "typescript": "^5.7.0" + } +} diff --git a/packages/recorder/src/constants.ts b/packages/recorder/src/constants.ts new file mode 100644 index 00000000..4e665140 --- /dev/null +++ b/packages/recorder/src/constants.ts @@ -0,0 +1,4 @@ +export const EVENT_COLLECT_INTERVAL_MS = 500; + +export const REPLAY_PLAYER_WIDTH_PX = 960; +export const REPLAY_PLAYER_HEIGHT_PX = 540; diff --git a/packages/recorder/src/errors.ts b/packages/recorder/src/errors.ts new file mode 100644 index 00000000..6a421e94 --- /dev/null +++ b/packages/recorder/src/errors.ts @@ -0,0 +1,18 @@ +import { Schema } from "effect"; + +export class RecorderInjectionError extends Schema.ErrorClass( + "RecorderInjectionError", +)({ + _tag: Schema.tag("RecorderInjectionError"), + cause: Schema.String, +}) { + message = `Failed to inject rrweb recorder: ${this.cause}`; +} + +export class SessionLoadError extends Schema.ErrorClass("SessionLoadError")({ + _tag: Schema.tag("SessionLoadError"), + path: Schema.String, + cause: Schema.String, +}) { + message = `Failed to load session from ${this.path}: ${this.cause}`; +} diff --git a/packages/recorder/src/index.ts b/packages/recorder/src/index.ts new file mode 100644 index 00000000..5b2151fb --- /dev/null +++ b/packages/recorder/src/index.ts @@ -0,0 +1,14 @@ +export { collectEvents, collectAllEvents, loadSession } from "./recorder"; +export { buildReplayViewerHtml } from "./replay-viewer"; +export { startLiveViewServer } from "./live-view-server"; +export type { LiveViewHandle, StartLiveViewServerOptions } from "./live-view-server"; +export { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; +export { RecorderInjectionError, SessionLoadError } from "./errors"; +export type { CollectResult } from "./types"; +export type { ViewerRunState, ViewerStepEvent } from "./viewer-events"; +export { + EVENT_COLLECT_INTERVAL_MS, + REPLAY_PLAYER_WIDTH_PX, + REPLAY_PLAYER_HEIGHT_PX, +} from "./constants"; +export type { eventWithTime } from "@rrweb/types"; diff --git a/packages/browser/src/mcp/live-view-server.ts b/packages/recorder/src/live-view-server.ts similarity index 96% rename from packages/browser/src/mcp/live-view-server.ts rename to packages/recorder/src/live-view-server.ts index d56db9ef..28529d8d 100644 --- a/packages/browser/src/mcp/live-view-server.ts +++ b/packages/recorder/src/live-view-server.ts @@ -2,9 +2,9 @@ import { createServer, type IncomingMessage, type Server, type ServerResponse } import type { Page } from "playwright"; import type { eventWithTime } from "@rrweb/types"; import { Effect, Fiber, Predicate, PubSub, Schedule, Stream } from "effect"; -import { EVENT_COLLECT_INTERVAL_MS } from "../constants"; -import { evaluateRuntime } from "../utils/evaluate-runtime"; -import { buildReplayViewerHtml } from "../replay-viewer"; +import { EVENT_COLLECT_INTERVAL_MS } from "./constants"; +import { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; +import { buildReplayViewerHtml } from "./replay-viewer"; import type { ViewerRunState } from "./viewer-events"; const isViewerRunState = (value: unknown): value is ViewerRunState => @@ -172,7 +172,7 @@ export const startLiveViewServer = Effect.fn("LiveViewServer.start")(function* ( const pollPage = Effect.sync(() => options.getPage()).pipe( Effect.flatMap((page) => { if (!page || page.isClosed()) return Effect.void; - return evaluateRuntime(page, "getEvents").pipe( + return evaluateRecorderRuntime(page, "getEvents").pipe( Effect.tap((events) => Effect.sync(() => { if (Array.isArray(events) && events.length > 0) { diff --git a/packages/browser/src/recorder.ts b/packages/recorder/src/recorder.ts similarity index 87% rename from packages/browser/src/recorder.ts rename to packages/recorder/src/recorder.ts index 9aa19607..fc4641aa 100644 --- a/packages/browser/src/recorder.ts +++ b/packages/recorder/src/recorder.ts @@ -2,15 +2,15 @@ import type { Page } from "playwright"; import type { eventWithTime } from "@rrweb/types"; import { Effect, Predicate } from "effect"; import { FileSystem } from "effect/FileSystem"; -import { evaluateRuntime } from "./utils/evaluate-runtime"; +import { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; import { RecorderInjectionError, SessionLoadError } from "./errors"; import type { CollectResult } from "./types"; export const collectEvents = Effect.fn("Recorder.collectEvents")(function* (page: Page) { - const events = yield* evaluateRuntime(page, "getEvents").pipe( + const events = yield* evaluateRecorderRuntime(page, "getEvents").pipe( Effect.catchCause((cause) => new RecorderInjectionError({ cause: String(cause) }).asEffect()), ); - const total = yield* evaluateRuntime(page, "getEventCount").pipe( + const total = yield* evaluateRecorderRuntime(page, "getEventCount").pipe( Effect.catchCause((cause) => new RecorderInjectionError({ cause: String(cause) }).asEffect()), ); @@ -18,7 +18,7 @@ export const collectEvents = Effect.fn("Recorder.collectEvents")(function* (page }); export const collectAllEvents = Effect.fn("Recorder.collectAllEvents")(function* (page: Page) { - return yield* evaluateRuntime(page, "getAllEvents").pipe( + return yield* evaluateRecorderRuntime(page, "getAllEvents").pipe( Effect.catchCause((cause) => new RecorderInjectionError({ cause: String(cause) }).asEffect()), ); }); diff --git a/packages/browser/src/replay-viewer.ts b/packages/recorder/src/replay-viewer.ts similarity index 99% rename from packages/browser/src/replay-viewer.ts rename to packages/recorder/src/replay-viewer.ts index c7ebce9d..58f7ad7b 100644 --- a/packages/browser/src/replay-viewer.ts +++ b/packages/recorder/src/replay-viewer.ts @@ -1,5 +1,5 @@ import { REPLAY_PLAYER_HEIGHT_PX, REPLAY_PLAYER_WIDTH_PX } from "./constants"; -import type { ViewerRunState } from "./mcp/viewer-events"; +import type { ViewerRunState } from "./viewer-events"; const escapeHtml = (text: string): string => text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); diff --git a/packages/recorder/src/runtime/index.ts b/packages/recorder/src/runtime/index.ts new file mode 100644 index 00000000..7b5eeb20 --- /dev/null +++ b/packages/recorder/src/runtime/index.ts @@ -0,0 +1,32 @@ +import { record } from "rrweb"; +import type { eventWithTime } from "@rrweb/types"; + +const eventBuffer: eventWithTime[] = []; +let stopFn: (() => void) | undefined; + +export const startRecording = (): void => { + eventBuffer.length = 0; + stopFn = + record({ + emit(event) { + eventBuffer.push(event); + }, + }) ?? undefined; +}; + +export const stopRecording = (): void => { + stopFn?.(); + stopFn = undefined; +}; + +export const getEvents = (): eventWithTime[] => { + return eventBuffer.splice(0); +}; + +export const getAllEvents = (): eventWithTime[] => { + return [...eventBuffer]; +}; + +export const getEventCount = (): number => { + return eventBuffer.length; +}; diff --git a/packages/recorder/src/types.ts b/packages/recorder/src/types.ts new file mode 100644 index 00000000..8a851e36 --- /dev/null +++ b/packages/recorder/src/types.ts @@ -0,0 +1,6 @@ +import type { eventWithTime } from "@rrweb/types"; + +export interface CollectResult { + readonly events: ReadonlyArray; + readonly total: number; +} diff --git a/packages/recorder/src/utils/evaluate-runtime.ts b/packages/recorder/src/utils/evaluate-runtime.ts new file mode 100644 index 00000000..d5b2079e --- /dev/null +++ b/packages/recorder/src/utils/evaluate-runtime.ts @@ -0,0 +1,29 @@ +import type { Page } from "playwright"; +import { Effect } from "effect"; +import type { RecorderRuntime } from "../generated/runtime-types"; + +// HACK: page.evaluate erases types across the serialization boundary; casts are confined here +export const evaluateRecorderRuntime = ( + page: Page, + method: K, + ...args: Parameters +) => + Effect.promise( + () => + page.evaluate( + ({ method, args }: { method: string; args: unknown[] }) => { + const runtime = Reflect.get(globalThis, "__browserTesterRuntime"); + if (!runtime || typeof runtime !== "object") { + throw new Error("Browser runtime is not initialized"); + } + + const fn = Reflect.get(runtime, method); + if (typeof fn !== "function") { + throw new Error(`Recorder runtime method not found: ${method}`); + } + + return fn(...args); + }, + { method, args: args as unknown[] }, + ) as Promise>, + ); diff --git a/packages/browser/src/mcp/viewer-events.ts b/packages/recorder/src/viewer-events.ts similarity index 100% rename from packages/browser/src/mcp/viewer-events.ts rename to packages/recorder/src/viewer-events.ts diff --git a/packages/browser/tests/live-view-server.test.ts b/packages/recorder/tests/live-view-server.test.ts similarity index 99% rename from packages/browser/tests/live-view-server.test.ts rename to packages/recorder/tests/live-view-server.test.ts index ff42f838..3141db8c 100644 --- a/packages/browser/tests/live-view-server.test.ts +++ b/packages/recorder/tests/live-view-server.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from "vite-plus/test"; import { Effect } from "effect"; -import { startLiveViewServer, type LiveViewHandle } from "../src/mcp/live-view-server"; +import { startLiveViewServer, type LiveViewHandle } from "../src/live-view-server"; const findAvailablePort = async (): Promise => { const { createServer } = await import("node:http"); diff --git a/packages/browser/tests/recorder.test.ts b/packages/recorder/tests/recorder.test.ts similarity index 100% rename from packages/browser/tests/recorder.test.ts rename to packages/recorder/tests/recorder.test.ts diff --git a/packages/recorder/tsconfig.json b/packages/recorder/tsconfig.json new file mode 100644 index 00000000..57bf531c --- /dev/null +++ b/packages/recorder/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/recorder/vite.config.ts b/packages/recorder/vite.config.ts new file mode 100644 index 00000000..90507fc8 --- /dev/null +++ b/packages/recorder/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vite-plus"; + +export default defineConfig({ + pack: { + entry: ["src/index.ts"], + format: ["esm"], + dts: true, + sourcemap: true, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97c5cd98..c5f84ce7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,15 +33,15 @@ importers: apps/cli: dependencies: - '@effect/atom-react': - specifier: 4.0.0-beta.35 - version: 4.0.0-beta.35(effect@4.0.0-beta.28)(react@19.2.4)(scheduler@0.27.0) - '@expect/shared': + '@browser-tester/shared': specifier: workspace:* version: link:../../packages/shared - '@expect/supervisor': + '@browser-tester/supervisor': specifier: workspace:* version: link:../../packages/supervisor + '@effect/atom-react': + specifier: 4.0.0-beta.35 + version: 4.0.0-beta.35(effect@4.0.0-beta.28)(react@19.2.4)(scheduler@0.27.0) '@tanstack/react-query': specifier: ^5.80.7 version: 5.90.21(react@19.2.4) @@ -179,10 +179,10 @@ importers: '@ai-sdk/provider': specifier: ^3.0.8 version: 3.0.8 - '@expect/browser': + '@browser-tester/browser': specifier: workspace:* version: link:../browser - '@expect/shared': + '@browser-tester/shared': specifier: workspace:* version: link:../shared '@zed-industries/claude-agent-acp': @@ -207,12 +207,15 @@ importers: packages/browser: dependencies: + '@browser-tester/cookies': + specifier: workspace:* + version: link:../cookies + '@browser-tester/recorder': + specifier: workspace:* + version: link:../recorder '@effect/platform-node': specifier: 4.0.0-beta.28 version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) - '@expect/cookies': - specifier: workspace:* - version: link:../cookies '@medv/finder': specifier: ^4.0.2 version: 4.0.2 @@ -225,9 +228,6 @@ importers: playwright: specifier: ^1.52.0 version: 1.58.2 - rrweb: - specifier: ^2.0.0-alpha.18 - version: 2.0.0-alpha.20 zod: specifier: ^4.3.6 version: 4.3.6 @@ -273,9 +273,30 @@ importers: specifier: ^5.7.0 version: 5.9.3 - packages/expect: {} - - packages/expect-skill: {} + packages/recorder: + dependencies: + '@effect/platform-node': + specifier: 4.0.0-beta.28 + version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) + '@rrweb/types': + specifier: ^2.0.0-alpha.18 + version: 2.0.0-alpha.20 + effect: + specifier: 4.0.0-beta.28 + version: 4.0.0-beta.28 + playwright: + specifier: ^1.52.0 + version: 1.58.2 + rrweb: + specifier: ^2.0.0-alpha.18 + version: 2.0.0-alpha.20 + devDependencies: + '@types/node': + specifier: ^22.15.0 + version: 22.19.15 + typescript: + specifier: ^5.7.0 + version: 5.9.3 packages/shared: dependencies: @@ -295,18 +316,18 @@ importers: '@ai-sdk/provider': specifier: ^3.0.8 version: 3.0.8 - '@effect/platform-node': - specifier: 4.0.0-beta.28 - version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) - '@expect/agent': + '@browser-tester/agent': specifier: workspace:* version: link:../agent - '@expect/browser': + '@browser-tester/browser': specifier: workspace:* version: link:../browser - '@expect/shared': + '@browser-tester/shared': specifier: workspace:* version: link:../shared + '@effect/platform-node': + specifier: 4.0.0-beta.28 + version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) effect: specifier: 4.0.0-beta.28 version: 4.0.0-beta.28 @@ -327,6 +348,10 @@ importers: specifier: ^5.7.0 version: 5.9.3 + packages/testie: {} + + packages/testie-skill: {} + playground/minecraft-ui: dependencies: next: From aaea7f8fe85de862a8eae6451b2348eb8c50ae12 Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Mon, 23 Mar 2026 20:16:33 -0700 Subject: [PATCH 02/10] fix --- packages/recorder/package.json | 7 +- packages/recorder/src/index.ts | 7 +- packages/recorder/src/recorder.ts | 14 +- .../recorder/src/utils/evaluate-runtime.ts | 8 +- packages/recorder/src/viewer-events.ts | 25 +++ packages/recorder/tests/recorder.test.ts | 56 +++++++ packages/recorder/tests/replay-viewer.test.ts | 86 ++++++++++ packages/recorder/viewer/index.html | 12 ++ packages/recorder/viewer/src/main.ts | 92 +++++++++++ packages/recorder/viewer/src/steps.ts | 72 +++++++++ packages/recorder/viewer/src/style.css | 153 ++++++++++++++++++ pnpm-lock.yaml | 41 +++++ 12 files changed, 557 insertions(+), 16 deletions(-) create mode 100644 packages/recorder/tests/replay-viewer.test.ts create mode 100644 packages/recorder/viewer/index.html create mode 100644 packages/recorder/viewer/src/main.ts create mode 100644 packages/recorder/viewer/src/steps.ts create mode 100644 packages/recorder/viewer/src/style.css diff --git a/packages/recorder/package.json b/packages/recorder/package.json index badfe484..54edbb3f 100644 --- a/packages/recorder/package.json +++ b/packages/recorder/package.json @@ -3,7 +3,8 @@ "version": "0.0.1", "private": true, "files": [ - "dist" + "dist", + "viewer" ], "type": "module", "main": "./dist/index.mjs", @@ -34,7 +35,9 @@ "@rrweb/types": "^2.0.0-alpha.18", "effect": "4.0.0-beta.28", "playwright": "^1.52.0", - "rrweb": "^2.0.0-alpha.18" + "rrweb": "^2.0.0-alpha.18", + "rrweb-player": "^2.0.0-alpha.18", + "vite": "^7.3.1" }, "devDependencies": { "@types/node": "^22.15.0", diff --git a/packages/recorder/src/index.ts b/packages/recorder/src/index.ts index 5b2151fb..4f85490f 100644 --- a/packages/recorder/src/index.ts +++ b/packages/recorder/src/index.ts @@ -5,7 +5,12 @@ export type { LiveViewHandle, StartLiveViewServerOptions } from "./live-view-ser export { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; export { RecorderInjectionError, SessionLoadError } from "./errors"; export type { CollectResult } from "./types"; -export type { ViewerRunState, ViewerStepEvent } from "./viewer-events"; +export { + ViewerRunStateSchema, + ViewerStepEventSchema, + type ViewerRunState, + type ViewerStepEvent, +} from "./viewer-events"; export { EVENT_COLLECT_INTERVAL_MS, REPLAY_PLAYER_WIDTH_PX, diff --git a/packages/recorder/src/recorder.ts b/packages/recorder/src/recorder.ts index fc4641aa..cb3e8e45 100644 --- a/packages/recorder/src/recorder.ts +++ b/packages/recorder/src/recorder.ts @@ -3,24 +3,18 @@ import type { eventWithTime } from "@rrweb/types"; import { Effect, Predicate } from "effect"; import { FileSystem } from "effect/FileSystem"; import { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; -import { RecorderInjectionError, SessionLoadError } from "./errors"; +import { SessionLoadError } from "./errors"; import type { CollectResult } from "./types"; export const collectEvents = Effect.fn("Recorder.collectEvents")(function* (page: Page) { - const events = yield* evaluateRecorderRuntime(page, "getEvents").pipe( - Effect.catchCause((cause) => new RecorderInjectionError({ cause: String(cause) }).asEffect()), - ); - const total = yield* evaluateRecorderRuntime(page, "getEventCount").pipe( - Effect.catchCause((cause) => new RecorderInjectionError({ cause: String(cause) }).asEffect()), - ); + const events = yield* evaluateRecorderRuntime(page, "getEvents"); + const total = yield* evaluateRecorderRuntime(page, "getEventCount"); return { events, total: total + events.length } satisfies CollectResult; }); export const collectAllEvents = Effect.fn("Recorder.collectAllEvents")(function* (page: Page) { - return yield* evaluateRecorderRuntime(page, "getAllEvents").pipe( - Effect.catchCause((cause) => new RecorderInjectionError({ cause: String(cause) }).asEffect()), - ); + return yield* evaluateRecorderRuntime(page, "getAllEvents"); }); const isRrwebEvent = (value: unknown): value is eventWithTime => diff --git a/packages/recorder/src/utils/evaluate-runtime.ts b/packages/recorder/src/utils/evaluate-runtime.ts index d5b2079e..a6347a4f 100644 --- a/packages/recorder/src/utils/evaluate-runtime.ts +++ b/packages/recorder/src/utils/evaluate-runtime.ts @@ -1,6 +1,7 @@ import type { Page } from "playwright"; import { Effect } from "effect"; import type { RecorderRuntime } from "../generated/runtime-types"; +import { RecorderInjectionError } from "../errors"; // HACK: page.evaluate erases types across the serialization boundary; casts are confined here export const evaluateRecorderRuntime = ( @@ -8,8 +9,8 @@ export const evaluateRecorderRuntime = ( method: K, ...args: Parameters ) => - Effect.promise( - () => + Effect.tryPromise({ + try: () => page.evaluate( ({ method, args }: { method: string; args: unknown[] }) => { const runtime = Reflect.get(globalThis, "__browserTesterRuntime"); @@ -26,4 +27,5 @@ export const evaluateRecorderRuntime = ( }, { method, args: args as unknown[] }, ) as Promise>, - ); + catch: (cause) => new RecorderInjectionError({ cause: String(cause) }), + }); diff --git a/packages/recorder/src/viewer-events.ts b/packages/recorder/src/viewer-events.ts index 747d07fa..a4021334 100644 --- a/packages/recorder/src/viewer-events.ts +++ b/packages/recorder/src/viewer-events.ts @@ -1,3 +1,5 @@ +import { Schema } from "effect"; + export interface ViewerStepEvent { readonly stepId: string; readonly title: string; @@ -5,9 +7,32 @@ export interface ViewerStepEvent { readonly summary: string | undefined; } +export const ViewerStepEventSchema = Schema.Struct({ + stepId: Schema.String, + title: Schema.String, + status: Schema.Union([ + Schema.Literal("pending"), + Schema.Literal("active"), + Schema.Literal("passed"), + Schema.Literal("failed"), + ]), + summary: Schema.optional(Schema.String), +}); + export interface ViewerRunState { readonly title: string; readonly status: "running" | "passed" | "failed"; readonly summary: string | undefined; readonly steps: readonly ViewerStepEvent[]; } + +export const ViewerRunStateSchema = Schema.Struct({ + title: Schema.String, + status: Schema.Union([ + Schema.Literal("running"), + Schema.Literal("passed"), + Schema.Literal("failed"), + ]), + summary: Schema.optional(Schema.String), + steps: Schema.Array(ViewerStepEventSchema), +}); diff --git a/packages/recorder/tests/recorder.test.ts b/packages/recorder/tests/recorder.test.ts index 22fa36cc..62bbebc8 100644 --- a/packages/recorder/tests/recorder.test.ts +++ b/packages/recorder/tests/recorder.test.ts @@ -65,4 +65,60 @@ describe("loadSession", () => { expect(String(result.cause)).toContain("line 2"); } }); + + it("reads a single-event NDJSON file", async () => { + tempDir = mkdtempSync(join(tmpdir(), TEMP_DIR_PREFIX)); + const sessionPath = join(tempDir, "single.ndjson"); + const event = fakeEvent(2, 1000); + writeFileSync(sessionPath, JSON.stringify(event) + "\n"); + + const loaded = await run(loadSession(sessionPath)); + + expect(loaded).toHaveLength(1); + expect(loaded[0]).toEqual(event); + }); + + it("fails with SessionLoadError for event missing type field", async () => { + tempDir = mkdtempSync(join(tmpdir(), TEMP_DIR_PREFIX)); + const sessionPath = join(tempDir, "missing-type.ndjson"); + writeFileSync(sessionPath, '{"timestamp":1000,"data":{}}\n'); + + const result = await Effect.runPromiseExit( + loadSession(sessionPath).pipe(Effect.provide(NodeFileSystem.layer)), + ); + + expect(result._tag).toBe("Failure"); + if (result._tag === "Failure") { + expect(String(result.cause)).toContain("SessionLoadError"); + expect(String(result.cause)).toContain("line 1"); + } + }); + + it("fails with SessionLoadError for event missing timestamp field", async () => { + tempDir = mkdtempSync(join(tmpdir(), TEMP_DIR_PREFIX)); + const sessionPath = join(tempDir, "missing-timestamp.ndjson"); + writeFileSync(sessionPath, '{"type":2,"data":{}}\n'); + + const result = await Effect.runPromiseExit( + loadSession(sessionPath).pipe(Effect.provide(NodeFileSystem.layer)), + ); + + expect(result._tag).toBe("Failure"); + if (result._tag === "Failure") { + expect(String(result.cause)).toContain("SessionLoadError"); + expect(String(result.cause)).toContain("line 1"); + } + }); + + it("preserves all event fields through round-trip", async () => { + tempDir = mkdtempSync(join(tmpdir(), TEMP_DIR_PREFIX)); + const sessionPath = join(tempDir, "full.ndjson"); + const event = { type: 2, timestamp: 1000, data: { node: { id: 1 } } } as eventWithTime; + writeFileSync(sessionPath, JSON.stringify(event) + "\n"); + + const loaded = await run(loadSession(sessionPath)); + + expect(loaded[0]).toEqual(event); + expect((loaded[0] as Record).data).toEqual({ node: { id: 1 } }); + }); }); diff --git a/packages/recorder/tests/replay-viewer.test.ts b/packages/recorder/tests/replay-viewer.test.ts new file mode 100644 index 00000000..00f689f0 --- /dev/null +++ b/packages/recorder/tests/replay-viewer.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from "vite-plus/test"; +import { buildReplayViewerHtml } from "../src/replay-viewer"; + +describe("buildReplayViewerHtml", () => { + it("escapes HTML entities in title", () => { + const html = buildReplayViewerHtml({ title: '' }); + expect(html).toContain("<script>alert("xss")</script>"); + expect(html).not.toContain(" + + diff --git a/packages/recorder/viewer/src/main.ts b/packages/recorder/viewer/src/main.ts new file mode 100644 index 00000000..f8b38b77 --- /dev/null +++ b/packages/recorder/viewer/src/main.ts @@ -0,0 +1,92 @@ +import rrwebPlayer from "rrweb-player"; +import "rrweb-player/dist/style.css"; +import type { eventWithTime } from "@rrweb/types"; +import { updateSteps } from "./steps"; +import "./style.css"; + +const REPLAY_WIDTH = 960; +const REPLAY_HEIGHT = 540; + +const app = document.getElementById("app")!; + +app.innerHTML = ` +
+

Test Run

+
running
+ +
    +
    +
    +
    Loading replay\u2026
    +
    +`; + +const container = document.getElementById("replay-container")!; +const statusElement = document.getElementById("status"); +let player: rrwebPlayer | undefined; +let allEvents: eventWithTime[] = []; + +const initPlayer = (events: eventWithTime[]): void => { + if (player) { + player.getReplayer().addEvent(events.at(-1)!); + return; + } + if (events.length < 2) return; + + statusElement?.remove(); + player = new rrwebPlayer({ + target: container, + props: { + events, + width: REPLAY_WIDTH, + height: REPLAY_HEIGHT, + autoPlay: true, + showController: false, + liveMode: true, + }, + }); + player.getReplayer().startLive(); +}; + +const bootstrap = async (): Promise => { + const latestResponse = await fetch("/latest.json"); + if (latestResponse.ok) { + allEvents = await latestResponse.json(); + if (allEvents.length >= 2) initPlayer(allEvents); + } + + const stepsResponse = await fetch("/steps"); + if (stepsResponse.ok) { + const state = await stepsResponse.json(); + if (state && state.steps) updateSteps(state); + } + + const eventSource = new EventSource("/events"); + + eventSource.addEventListener("replay", (message) => { + try { + const events: eventWithTime[] = JSON.parse(message.data); + for (const event of events) { + allEvents.push(event); + if (player) player.getReplayer().addEvent(event); + } + if (!player && allEvents.length >= 2) initPlayer(allEvents); + } catch { + // ignore malformed events + } + }); + + eventSource.addEventListener("steps", (message) => { + try { + updateSteps(JSON.parse(message.data)); + } catch { + // ignore malformed steps + } + }); + + eventSource.onerror = () => { + if (statusElement) statusElement.textContent = "Connection lost. Retrying..."; + }; +}; + +bootstrap(); diff --git a/packages/recorder/viewer/src/steps.ts b/packages/recorder/viewer/src/steps.ts new file mode 100644 index 00000000..4e320bab --- /dev/null +++ b/packages/recorder/viewer/src/steps.ts @@ -0,0 +1,72 @@ +interface StepEvent { + readonly stepId: string; + readonly title: string; + readonly status: "pending" | "active" | "passed" | "failed"; + readonly summary: string | undefined; +} + +interface RunState { + readonly title: string; + readonly status: "running" | "passed" | "failed"; + readonly summary: string | undefined; + readonly steps: readonly StepEvent[]; +} + +const STATUS_BADGES: Record = { + passed: "\u2713", + failed: "\u2717", + active: "\u25CF", + pending: "\u25CB", +}; + +export const updateSteps = (state: RunState): void => { + const stepsPanel = document.getElementById("steps-panel"); + const runTitle = document.getElementById("run-title"); + const runStatus = document.getElementById("run-status"); + const runSummary = document.getElementById("run-summary"); + const stepsList = document.getElementById("steps-list"); + + if (!stepsPanel || !state) return; + + stepsPanel.style.display = "block"; + + if (runTitle) runTitle.textContent = state.title || "Test Run"; + + if (runStatus) { + runStatus.textContent = state.status; + runStatus.className = `run-status status-${state.status}`; + } + + if (runSummary) { + runSummary.textContent = state.summary ?? ""; + runSummary.style.display = state.summary ? "block" : "none"; + } + + if (stepsList && state.steps) { + stepsList.innerHTML = ""; + for (const step of state.steps) { + const listItem = document.createElement("li"); + listItem.className = `step-item step-${step.status}`; + + const badge = document.createElement("span"); + badge.className = "step-badge"; + badge.textContent = STATUS_BADGES[step.status]; + + const title = document.createElement("span"); + title.className = "step-title"; + title.textContent = step.title; + + listItem.appendChild(badge); + listItem.appendChild(title); + + if (step.summary) { + const summary = document.createElement("span"); + summary.className = "step-summary"; + summary.textContent = step.summary; + listItem.appendChild(summary); + } + + stepsList.appendChild(listItem); + } + } +}; diff --git a/packages/recorder/viewer/src/style.css b/packages/recorder/viewer/src/style.css new file mode 100644 index 00000000..69918a36 --- /dev/null +++ b/packages/recorder/viewer/src/style.css @@ -0,0 +1,153 @@ +:root { + color-scheme: dark; +} + +body { + margin: 0; + font-family: ui-sans-serif, system-ui, sans-serif; + background: #0f172a; + color: #e2e8f0; +} + +h1, +h2 { + color: #f8fafc; +} + +a { + color: #93c5fd; +} + +#app { + max-width: 960px; + margin: 0 auto; + padding: 32px; +} + +#replay-container { + margin: 16px 0; + border-radius: 8px; + overflow: hidden; +} + +.status { + text-align: center; + padding: 16px; + font-size: 14px; + color: #9ca3af; +} + +#steps-panel { + display: none; + margin-bottom: 24px; + background: #1e293b; + border-radius: 8px; + padding: 20px; + border: 1px solid #334155; +} + +#steps-panel h2 { + margin: 0 0 4px 0; + font-size: 16px; + font-weight: 600; +} + +.run-status { + display: inline-block; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 2px 8px; + border-radius: 4px; + margin-bottom: 12px; +} + +.status-running { + background: #1e3a5f; + color: #60a5fa; +} + +.status-passed { + background: #14532d; + color: #4ade80; +} + +.status-failed { + background: #7f1d1d; + color: #f87171; +} + +#run-summary { + font-size: 13px; + color: #94a3b8; + margin-bottom: 16px; +} + +#steps-list { + list-style: none; + padding: 0; + margin: 0; +} + +.step-item { + display: flex; + align-items: baseline; + gap: 8px; + padding: 6px 0; + font-size: 14px; + border-top: 1px solid #1e293b; +} + +.step-item:first-child { + border-top: none; +} + +.step-badge { + flex-shrink: 0; + width: 16px; + text-align: center; + font-size: 13px; +} + +.step-pending .step-badge { + color: #64748b; +} + +.step-active .step-badge { + color: #60a5fa; + animation: pulse 1.5s infinite; +} + +.step-passed .step-badge { + color: #4ade80; +} + +.step-failed .step-badge { + color: #f87171; +} + +.step-title { + color: #e2e8f0; +} + +.step-summary { + color: #94a3b8; + font-size: 12px; + margin-left: auto; + max-width: 50%; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.4; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5f84ce7..84dd2cea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -290,6 +290,12 @@ importers: rrweb: specifier: ^2.0.0-alpha.18 version: 2.0.0-alpha.20 + rrweb-player: + specifier: ^2.0.0-alpha.18 + version: 2.0.0-alpha.20 + vite: + specifier: npm:@voidzero-dev/vite-plus-core@^0.1.12 + version: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' devDependencies: '@types/node': specifier: ^22.15.0 @@ -2522,6 +2528,12 @@ packages: cpu: [x64] os: [win32] + '@rrweb/packer@2.0.0-alpha.20': + resolution: {integrity: sha512-GsByg2olGZ2n3To6keFG604QAboipuXZvjYxO2wITSwARBf/sZdy6cbUEjF0RS+QnuTM5GaVXeQapNMLmpKbrA==} + + '@rrweb/replay@2.0.0-alpha.20': + resolution: {integrity: sha512-VodsLb+C2bYNNVbb0U14tKLa9ctzUxYIlt9VnxPATWvfyXHLTku8BhRWptuW/iIjVjmG49LBoR1ilxw/HMiJ1w==} + '@rrweb/types@2.0.0-alpha.20': resolution: {integrity: sha512-RbnDgKxA/odwB1R4gF7eUUj+rdSrq6ROQJsnMw7MIsGzlbSYvJeZN8YY4XqU0G6sKJvXI6bSzk7w/G94jNwzhw==} @@ -2644,6 +2656,9 @@ packages: '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} + '@tsconfig/svelte@1.0.13': + resolution: {integrity: sha512-5lYJP45Xllo4yE/RUBccBT32eBlRDbqN8r1/MIvQbKxW3aFqaYPCNgm8D5V20X4ShHcwvYWNlKg3liDh1MlBoA==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -3396,6 +3411,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fflate@0.4.8: + resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -4440,6 +4458,9 @@ packages: rrdom@2.0.0-alpha.20: resolution: {integrity: sha512-hoqjS4662LtBp82qEz9GrqU36UpEmCvTA2Hns3qdF7cklLFFy3G+0Th8hLytJENleHHWxsB5nWJ3eXz5mSRxdQ==} + rrweb-player@2.0.0-alpha.20: + resolution: {integrity: sha512-3ZCv1ksUxuIOn3Vn/eWrwWs9Xy+4KVjISD+q26ZLfisZ3hZ0CPgYG3iC22pmmycIeMS2svOfvf7gPh7jExwpUA==} + rrweb-snapshot@2.0.0-alpha.20: resolution: {integrity: sha512-YTNf9YVeaGRo/jxY3FKBge2c/Ojd/KTHmuWloUSB+oyPXuY73ZeeG873qMMmhIpqEn7hn7aBF1eWEQmP7wjf8A==} @@ -6940,6 +6961,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true + '@rrweb/packer@2.0.0-alpha.20': + dependencies: + '@rrweb/types': 2.0.0-alpha.20 + fflate: 0.4.8 + + '@rrweb/replay@2.0.0-alpha.20': + dependencies: + '@rrweb/types': 2.0.0-alpha.20 + rrweb: 2.0.0-alpha.20 + '@rrweb/types@2.0.0-alpha.20': {} '@rrweb/utils@2.0.0-alpha.20': {} @@ -7036,6 +7067,8 @@ snapshots: minimatch: 10.2.4 path-browserify: 1.0.1 + '@tsconfig/svelte@1.0.13': {} + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -7719,6 +7752,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fflate@0.4.8: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -8771,6 +8806,12 @@ snapshots: dependencies: rrweb-snapshot: 2.0.0-alpha.20 + rrweb-player@2.0.0-alpha.20: + dependencies: + '@rrweb/packer': 2.0.0-alpha.20 + '@rrweb/replay': 2.0.0-alpha.20 + '@tsconfig/svelte': 1.0.13 + rrweb-snapshot@2.0.0-alpha.20: dependencies: postcss: 8.5.8 From 4c5e864f635bbbc564e5450eb9da7f49d1714047 Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Mon, 23 Mar 2026 20:54:08 -0700 Subject: [PATCH 03/10] fix --- packages/browser/scripts/build-runtime.js | 7 +- packages/browser/src/index.ts | 9 - packages/browser/src/mcp/index.ts | 1 - packages/recorder/package.json | 13 + packages/recorder/src/index.ts | 7 +- packages/recorder/viewer/components.json | 23 + packages/recorder/viewer/index.html | 4 +- packages/recorder/viewer/src/app.tsx | 95 ++++ .../viewer/src/hooks/use-mount-effect.ts | 6 + packages/recorder/viewer/src/lib/utils.ts | 4 + packages/recorder/viewer/src/main.ts | 92 ---- packages/recorder/viewer/src/main.tsx | 10 + packages/recorder/viewer/src/steps-panel.tsx | 72 +++ packages/recorder/viewer/src/steps.ts | 72 --- packages/recorder/viewer/src/style.css | 300 +++++------ packages/recorder/viewer/tsconfig.json | 17 + packages/recorder/viewer/vite.config.ts | 13 + pnpm-lock.yaml | 470 +++++++++++++++++- 18 files changed, 878 insertions(+), 337 deletions(-) create mode 100644 packages/recorder/viewer/components.json create mode 100644 packages/recorder/viewer/src/app.tsx create mode 100644 packages/recorder/viewer/src/hooks/use-mount-effect.ts create mode 100644 packages/recorder/viewer/src/lib/utils.ts delete mode 100644 packages/recorder/viewer/src/main.ts create mode 100644 packages/recorder/viewer/src/main.tsx create mode 100644 packages/recorder/viewer/src/steps-panel.tsx delete mode 100644 packages/recorder/viewer/src/steps.ts create mode 100644 packages/recorder/viewer/tsconfig.json create mode 100644 packages/recorder/viewer/vite.config.ts diff --git a/packages/browser/scripts/build-runtime.js b/packages/browser/scripts/build-runtime.js index e4c33cc3..dbed8c96 100644 --- a/packages/browser/scripts/build-runtime.js +++ b/packages/browser/scripts/build-runtime.js @@ -45,9 +45,10 @@ const emitPlugin = { const exportNames = extractExportedFunctionNames(source); fs.writeFileSync("src/generated/runtime-types.ts", generateRuntimeTypes(exportNames)); - execSync("npx vp fmt src/generated/runtime-script.ts src/generated/runtime-types.ts", { - stdio: "ignore", - }); + execSync( + "pnpm run format -- src/generated/runtime-script.ts src/generated/runtime-types.ts", + { stdio: "ignore" }, + ); }); }, }; diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 68dc9f02..091dd87f 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -31,12 +31,3 @@ export type { SnapshotResult, SnapshotStats, } from "./types"; -export { - buildReplayViewerHtml, - collectEvents, - collectAllEvents, - loadSession, - RecorderInjectionError, - SessionLoadError, -} from "@browser-tester/recorder"; -export type { CollectResult } from "@browser-tester/recorder"; diff --git a/packages/browser/src/mcp/index.ts b/packages/browser/src/mcp/index.ts index 34c4b74a..56858692 100644 --- a/packages/browser/src/mcp/index.ts +++ b/packages/browser/src/mcp/index.ts @@ -2,4 +2,3 @@ export { EXPECT_LIVE_VIEW_URL_ENV_NAME, EXPECT_REPLAY_OUTPUT_ENV_NAME } from "./ export { McpSession } from "./mcp-session"; export { McpRuntime } from "./runtime"; export { createBrowserMcpServer, startBrowserMcpServer } from "./server"; -export type { ViewerRunState, ViewerStepEvent } from "@browser-tester/recorder"; diff --git a/packages/recorder/package.json b/packages/recorder/package.json index 54edbb3f..bb1a5842 100644 --- a/packages/recorder/package.json +++ b/packages/recorder/package.json @@ -40,7 +40,20 @@ "vite": "^7.3.1" }, "devDependencies": { + "@tailwindcss/vite": "^4.2.2", "@types/node": "^22.15.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.576.0", + "react": "^19.2.4", + "react-dom": "19.2.3", + "shadcn": "^3.8.5", + "tailwind-merge": "^3.5.0", + "tailwindcss": "^4.2.1", + "tw-animate-css": "^1.4.0", "typescript": "^5.7.0" } } diff --git a/packages/recorder/src/index.ts b/packages/recorder/src/index.ts index 4f85490f..5b2151fb 100644 --- a/packages/recorder/src/index.ts +++ b/packages/recorder/src/index.ts @@ -5,12 +5,7 @@ export type { LiveViewHandle, StartLiveViewServerOptions } from "./live-view-ser export { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; export { RecorderInjectionError, SessionLoadError } from "./errors"; export type { CollectResult } from "./types"; -export { - ViewerRunStateSchema, - ViewerStepEventSchema, - type ViewerRunState, - type ViewerStepEvent, -} from "./viewer-events"; +export type { ViewerRunState, ViewerStepEvent } from "./viewer-events"; export { EVENT_COLLECT_INTERVAL_MS, REPLAY_PLAYER_WIDTH_PX, diff --git a/packages/recorder/viewer/components.json b/packages/recorder/viewer/components.json new file mode 100644 index 00000000..d111985e --- /dev/null +++ b/packages/recorder/viewer/components.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/style.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/packages/recorder/viewer/index.html b/packages/recorder/viewer/index.html index 8cb1abfb..7e3401e3 100644 --- a/packages/recorder/viewer/index.html +++ b/packages/recorder/viewer/index.html @@ -1,5 +1,5 @@ - + @@ -7,6 +7,6 @@
    - + diff --git a/packages/recorder/viewer/src/app.tsx b/packages/recorder/viewer/src/app.tsx new file mode 100644 index 00000000..4a801c17 --- /dev/null +++ b/packages/recorder/viewer/src/app.tsx @@ -0,0 +1,95 @@ +import { useCallback, useRef, useState } from "react"; +import rrwebPlayer from "rrweb-player"; +import "rrweb-player/dist/style.css"; +import type { eventWithTime } from "@rrweb/types"; +import { REPLAY_PLAYER_HEIGHT_PX, REPLAY_PLAYER_WIDTH_PX } from "../../src/constants"; +import type { ViewerRunState } from "../../src/viewer-events"; +import { useMountEffect } from "./hooks/use-mount-effect"; +import { StepsPanel } from "./steps-panel"; + +export const App = () => { + const [runState, setRunState] = useState(); + const [status, setStatus] = useState("Loading replay\u2026"); + const containerRef = useRef(null); + const playerRef = useRef(); + const eventsRef = useRef([]); + + const initPlayer = useCallback((events: eventWithTime[]) => { + if (playerRef.current) { + playerRef.current.getReplayer().addEvent(events.at(-1)!); + return; + } + if (events.length < 2 || !containerRef.current) return; + + setStatus(""); + playerRef.current = new rrwebPlayer({ + target: containerRef.current, + props: { + events, + width: REPLAY_PLAYER_WIDTH_PX, + height: REPLAY_PLAYER_HEIGHT_PX, + autoPlay: true, + showController: false, + liveMode: true, + }, + }); + playerRef.current.getReplayer().startLive(); + }, []); + + useMountEffect(() => { + const bootstrap = async () => { + const latestResponse = await fetch("/latest.json"); + if (latestResponse.ok) { + eventsRef.current = await latestResponse.json(); + if (eventsRef.current.length >= 2) initPlayer(eventsRef.current); + } + + const stepsResponse = await fetch("/steps"); + if (stepsResponse.ok) { + const state = await stepsResponse.json(); + if (state?.steps) setRunState(state); + } + + const eventSource = new EventSource("/events"); + + eventSource.addEventListener("replay", (message) => { + try { + const events: eventWithTime[] = JSON.parse(message.data); + for (const event of events) { + eventsRef.current.push(event); + if (playerRef.current) playerRef.current.getReplayer().addEvent(event); + } + if (!playerRef.current && eventsRef.current.length >= 2) { + initPlayer(eventsRef.current); + } + } catch { + /* ignore malformed events */ + } + }); + + eventSource.addEventListener("steps", (message) => { + try { + setRunState(JSON.parse(message.data)); + } catch { + /* ignore malformed steps */ + } + }); + + eventSource.onerror = () => { + setStatus("Connection lost. Retrying..."); + }; + }; + + bootstrap(); + }); + + return ( +
    + +
    + {status &&
    {status}
    } +
    +
    +
    + ); +}; diff --git a/packages/recorder/viewer/src/hooks/use-mount-effect.ts b/packages/recorder/viewer/src/hooks/use-mount-effect.ts new file mode 100644 index 00000000..9e9c1530 --- /dev/null +++ b/packages/recorder/viewer/src/hooks/use-mount-effect.ts @@ -0,0 +1,6 @@ +/* oxlint-disable no-restricted-imports, react-hooks/exhaustive-deps */ +import { useEffect } from "react"; + +export const useMountEffect = (effect: () => void | (() => void)) => { + useEffect(effect, []); +}; diff --git a/packages/recorder/viewer/src/lib/utils.ts b/packages/recorder/viewer/src/lib/utils.ts new file mode 100644 index 00000000..a500a738 --- /dev/null +++ b/packages/recorder/viewer/src/lib/utils.ts @@ -0,0 +1,4 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs)); diff --git a/packages/recorder/viewer/src/main.ts b/packages/recorder/viewer/src/main.ts deleted file mode 100644 index f8b38b77..00000000 --- a/packages/recorder/viewer/src/main.ts +++ /dev/null @@ -1,92 +0,0 @@ -import rrwebPlayer from "rrweb-player"; -import "rrweb-player/dist/style.css"; -import type { eventWithTime } from "@rrweb/types"; -import { updateSteps } from "./steps"; -import "./style.css"; - -const REPLAY_WIDTH = 960; -const REPLAY_HEIGHT = 540; - -const app = document.getElementById("app")!; - -app.innerHTML = ` -
    -

    Test Run

    -
    running
    - -
      -
      -
      -
      Loading replay\u2026
      -
      -`; - -const container = document.getElementById("replay-container")!; -const statusElement = document.getElementById("status"); -let player: rrwebPlayer | undefined; -let allEvents: eventWithTime[] = []; - -const initPlayer = (events: eventWithTime[]): void => { - if (player) { - player.getReplayer().addEvent(events.at(-1)!); - return; - } - if (events.length < 2) return; - - statusElement?.remove(); - player = new rrwebPlayer({ - target: container, - props: { - events, - width: REPLAY_WIDTH, - height: REPLAY_HEIGHT, - autoPlay: true, - showController: false, - liveMode: true, - }, - }); - player.getReplayer().startLive(); -}; - -const bootstrap = async (): Promise => { - const latestResponse = await fetch("/latest.json"); - if (latestResponse.ok) { - allEvents = await latestResponse.json(); - if (allEvents.length >= 2) initPlayer(allEvents); - } - - const stepsResponse = await fetch("/steps"); - if (stepsResponse.ok) { - const state = await stepsResponse.json(); - if (state && state.steps) updateSteps(state); - } - - const eventSource = new EventSource("/events"); - - eventSource.addEventListener("replay", (message) => { - try { - const events: eventWithTime[] = JSON.parse(message.data); - for (const event of events) { - allEvents.push(event); - if (player) player.getReplayer().addEvent(event); - } - if (!player && allEvents.length >= 2) initPlayer(allEvents); - } catch { - // ignore malformed events - } - }); - - eventSource.addEventListener("steps", (message) => { - try { - updateSteps(JSON.parse(message.data)); - } catch { - // ignore malformed steps - } - }); - - eventSource.onerror = () => { - if (statusElement) statusElement.textContent = "Connection lost. Retrying..."; - }; -}; - -bootstrap(); diff --git a/packages/recorder/viewer/src/main.tsx b/packages/recorder/viewer/src/main.tsx new file mode 100644 index 00000000..172bfca8 --- /dev/null +++ b/packages/recorder/viewer/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./app"; +import "./style.css"; + +createRoot(document.getElementById("app")!).render( + + + , +); diff --git a/packages/recorder/viewer/src/steps-panel.tsx b/packages/recorder/viewer/src/steps-panel.tsx new file mode 100644 index 00000000..c310866d --- /dev/null +++ b/packages/recorder/viewer/src/steps-panel.tsx @@ -0,0 +1,72 @@ +import type { ViewerRunState, ViewerStepEvent } from "../../src/viewer-events"; +import { cn } from "@/lib/utils"; + +const STATUS_BADGES: Record = { + passed: "\u2713", + failed: "\u2717", + active: "\u25CF", + pending: "\u25CB", +}; + +const STATUS_BADGE_COLORS: Record = { + pending: "text-muted-foreground", + active: "text-blue-400 animate-pulse", + passed: "text-green-400", + failed: "text-red-400", +}; + +const RUN_STATUS_STYLES: Record = { + running: "bg-blue-950 text-blue-400", + passed: "bg-green-950 text-green-400", + failed: "bg-red-950 text-red-400", +}; + +interface StepsPanelProps { + readonly state: ViewerRunState | undefined; +} + +export const StepsPanel = ({ state }: StepsPanelProps) => { + if (!state) return null; + + return ( +
      +

      + {state.title || "Test Run"} +

      +
      + {state.status} +
      + {state.summary && ( +
      {state.summary}
      + )} +
        + {state.steps.map((step) => ( +
      • + + {STATUS_BADGES[step.status]} + + {step.title} + {step.summary && ( + + {step.summary} + + )} +
      • + ))} +
      +
      + ); +}; diff --git a/packages/recorder/viewer/src/steps.ts b/packages/recorder/viewer/src/steps.ts deleted file mode 100644 index 4e320bab..00000000 --- a/packages/recorder/viewer/src/steps.ts +++ /dev/null @@ -1,72 +0,0 @@ -interface StepEvent { - readonly stepId: string; - readonly title: string; - readonly status: "pending" | "active" | "passed" | "failed"; - readonly summary: string | undefined; -} - -interface RunState { - readonly title: string; - readonly status: "running" | "passed" | "failed"; - readonly summary: string | undefined; - readonly steps: readonly StepEvent[]; -} - -const STATUS_BADGES: Record = { - passed: "\u2713", - failed: "\u2717", - active: "\u25CF", - pending: "\u25CB", -}; - -export const updateSteps = (state: RunState): void => { - const stepsPanel = document.getElementById("steps-panel"); - const runTitle = document.getElementById("run-title"); - const runStatus = document.getElementById("run-status"); - const runSummary = document.getElementById("run-summary"); - const stepsList = document.getElementById("steps-list"); - - if (!stepsPanel || !state) return; - - stepsPanel.style.display = "block"; - - if (runTitle) runTitle.textContent = state.title || "Test Run"; - - if (runStatus) { - runStatus.textContent = state.status; - runStatus.className = `run-status status-${state.status}`; - } - - if (runSummary) { - runSummary.textContent = state.summary ?? ""; - runSummary.style.display = state.summary ? "block" : "none"; - } - - if (stepsList && state.steps) { - stepsList.innerHTML = ""; - for (const step of state.steps) { - const listItem = document.createElement("li"); - listItem.className = `step-item step-${step.status}`; - - const badge = document.createElement("span"); - badge.className = "step-badge"; - badge.textContent = STATUS_BADGES[step.status]; - - const title = document.createElement("span"); - title.className = "step-title"; - title.textContent = step.title; - - listItem.appendChild(badge); - listItem.appendChild(title); - - if (step.summary) { - const summary = document.createElement("span"); - summary.className = "step-summary"; - summary.textContent = step.summary; - listItem.appendChild(summary); - } - - stepsList.appendChild(listItem); - } - } -}; diff --git a/packages/recorder/viewer/src/style.css b/packages/recorder/viewer/src/style.css index 69918a36..1f0cc895 100644 --- a/packages/recorder/viewer/src/style.css +++ b/packages/recorder/viewer/src/style.css @@ -1,153 +1,163 @@ -:root { - color-scheme: dark; -} - -body { - margin: 0; - font-family: ui-sans-serif, system-ui, sans-serif; - background: #0f172a; - color: #e2e8f0; -} - -h1, -h2 { - color: #f8fafc; -} - -a { - color: #93c5fd; -} - -#app { - max-width: 960px; - margin: 0 auto; - padding: 32px; -} - -#replay-container { - margin: 16px 0; - border-radius: 8px; - overflow: hidden; -} - -.status { - text-align: center; - padding: 16px; - font-size: 14px; - color: #9ca3af; -} - -#steps-panel { - display: none; - margin-bottom: 24px; - background: #1e293b; - border-radius: 8px; - padding: 20px; - border: 1px solid #334155; -} - -#steps-panel h2 { - margin: 0 0 4px 0; - font-size: 16px; - font-weight: 600; -} - -.run-status { - display: inline-block; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - padding: 2px 8px; - border-radius: 4px; - margin-bottom: 12px; -} - -.status-running { - background: #1e3a5f; - color: #60a5fa; -} - -.status-passed { - background: #14532d; - color: #4ade80; -} - -.status-failed { - background: #7f1d1d; - color: #f87171; +@import "tailwindcss"; +@import "tw-animate-css"; +@import "shadcn/tailwind.css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: system-ui, -apple-system, sans-serif; + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-xs: 0.4375rem; + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --radius-2xl: calc(var(--radius) + 8px); + --radius-3xl: calc(var(--radius) + 12px); + --radius-4xl: calc(var(--radius) + 16px); + --spacing-2.25: 0.5625rem; + --spacing-3.25: 0.8125rem; + --spacing-3.75: 0.9375rem; + --spacing-5.75: 1.4375rem; + --text-caption: 0.8125rem; } -#run-summary { - font-size: 13px; - color: #94a3b8; - margin-bottom: 16px; -} - -#steps-list { - list-style: none; - padding: 0; - margin: 0; -} - -.step-item { - display: flex; - align-items: baseline; - gap: 8px; - padding: 6px 0; - font-size: 14px; - border-top: 1px solid #1e293b; -} - -.step-item:first-child { - border-top: none; -} - -.step-badge { - flex-shrink: 0; - width: 16px; - text-align: center; - font-size: 13px; -} - -.step-pending .step-badge { - color: #64748b; -} - -.step-active .step-badge { - color: #60a5fa; - animation: pulse 1.5s infinite; -} - -.step-passed .step-badge { - color: #4ade80; -} - -.step-failed .step-badge { - color: #f87171; -} +:root { + --radius: 0.625rem; + --background: #fbfbfb; + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0.004 60); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0.004 60); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(1 0 0); + --primary-foreground: oklch(0 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } -.step-title { - color: #e2e8f0; -} + html { + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-kerning: normal; + font-optical-sizing: auto; + font-synthesis: none; + } -.step-summary { - color: #94a3b8; - font-size: 12px; - margin-left: auto; - max-width: 50%; - text-align: right; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} + body { + @apply bg-background text-foreground; + font-family: + system-ui, + -apple-system, + sans-serif; + font-weight: 400; + letter-spacing: -0.02em; + } -@keyframes pulse { - 0%, - 100% { - opacity: 1; + input, + textarea, + select, + button { + font-kerning: inherit; + font-optical-sizing: inherit; } - 50% { - opacity: 0.4; + + code, + kbd, + pre, + samp, + .font-mono { + letter-spacing: 0; } } diff --git a/packages/recorder/viewer/tsconfig.json b/packages/recorder/viewer/tsconfig.json new file mode 100644 index 00000000..ee26c648 --- /dev/null +++ b/packages/recorder/viewer/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/packages/recorder/viewer/vite.config.ts b/packages/recorder/viewer/vite.config.ts new file mode 100644 index 00000000..ab008a09 --- /dev/null +++ b/packages/recorder/viewer/vite.config.ts @@ -0,0 +1,13 @@ +import { resolve } from "node:path"; +import tailwindcss from "@tailwindcss/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [tailwindcss(), react()], + resolve: { + alias: { + "@": resolve(import.meta.dirname, "src"), + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84dd2cea..8a3da867 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,7 +29,7 @@ importers: version: 5.9.3 vite-plus: specifier: ^0.1.12 - version: 0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2) + version: 0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2) apps/cli: dependencies: @@ -297,9 +297,48 @@ importers: specifier: npm:@voidzero-dev/vite-plus-core@^0.1.12 version: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' devDependencies: + '@tailwindcss/vite': + specifier: ^4.2.2 + version: 4.2.2(@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)) '@types/node': specifier: ^22.15.0 version: 22.19.15 + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(babel-plugin-react-compiler@1.0.0) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.576.0 + version: 0.576.0(react@19.2.4) + react: + specifier: ^19.2.4 + version: 19.2.4 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.4) + shadcn: + specifier: ^3.8.5 + version: 3.8.5(@types/node@22.19.15)(typescript@5.9.3) + tailwind-merge: + specifier: ^3.5.0 + version: 3.5.0 + tailwindcss: + specifier: ^4.2.1 + version: 4.2.1 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 typescript: specifier: ^5.7.0 version: 5.9.3 @@ -2390,6 +2429,9 @@ packages: resolution: {integrity: sha512-IE4bTeH0mCq0FBRaYRUtdiIfvO7NCv14lHS4TiTY8YNG5tPYwHnLZZtniud0ElmLIWGP95Kk6E7A6J2uDmOY+Q==} hasBin: true + '@rolldown/pluginutils@1.0.0-rc.7': + resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} + '@rollup/rollup-android-arm-eabi@4.59.0': resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] @@ -2556,36 +2598,69 @@ packages: '@tailwindcss/node@4.2.1': resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} + '@tailwindcss/node@4.2.2': + resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + '@tailwindcss/oxide-android-arm64@4.2.1': resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] + '@tailwindcss/oxide-android-arm64@4.2.2': + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + '@tailwindcss/oxide-darwin-arm64@4.2.1': resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] + '@tailwindcss/oxide-darwin-arm64@4.2.2': + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.2.1': resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.2.2': + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + '@tailwindcss/oxide-freebsd-x64@4.2.1': resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] + '@tailwindcss/oxide-freebsd-x64@4.2.2': + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} engines: {node: '>= 20'} cpu: [arm] os: [linux] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} engines: {node: '>= 20'} @@ -2593,6 +2668,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} engines: {node: '>= 20'} @@ -2600,6 +2682,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} engines: {node: '>= 20'} @@ -2607,6 +2696,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-x64-musl@4.2.1': resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} engines: {node: '>= 20'} @@ -2614,6 +2710,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-wasm32-wasi@4.2.1': resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} engines: {node: '>=14.0.0'} @@ -2626,25 +2729,58 @@ packages: - '@emnapi/wasi-threads' - tslib + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} engines: {node: '>= 20'} cpu: [x64] os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + '@tailwindcss/oxide@4.2.1': resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} engines: {node: '>= 20'} + '@tailwindcss/oxide@4.2.2': + resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} + engines: {node: '>= 20'} + '@tailwindcss/postcss@4.2.1': resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==} + '@tailwindcss/vite@4.2.2': + resolution: {integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + '@tanstack/query-core@5.90.20': resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} @@ -2744,6 +2880,19 @@ packages: resolution: {integrity: sha512-K9evb5u4QmH3Xv2XUg9OWUETYMrIX1C7Hls1ce8DW+Nlbb26NnQ5SPQCt8fGq4FGqZ9BodMwfane1pTd+BWYwQ==} hasBin: true + '@vitejs/plugin-react@6.0.1': + resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + '@voidzero-dev/vite-plus-core@0.1.12': resolution: {integrity: sha512-j8YNe7A+8JcSoddztf5whvom/yJ7OKUO3Y5a3UoLIUmOL8YEKVv5nPANrxJ7eaFfHJoMnBEwzBpq1YVZ+H3uPA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3826,30 +3975,60 @@ packages: cpu: [arm64] os: [android] + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-darwin-arm64@1.31.1: resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.31.1: resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.31.1: resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.31.1: resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.31.1: resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} engines: {node: '>= 12.0.0'} @@ -3857,6 +4036,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + lightningcss-linux-arm64-musl@1.31.1: resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} @@ -3864,6 +4050,13 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + lightningcss-linux-x64-gnu@1.31.1: resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} @@ -3871,6 +4064,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + lightningcss-linux-x64-musl@1.31.1: resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} @@ -3878,22 +4078,45 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + lightningcss-win32-arm64-msvc@1.31.1: resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.31.1: resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss@1.31.1: resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} engines: {node: '>= 12.0.0'} + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4683,6 +4906,9 @@ packages: tailwindcss@4.2.1: resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} + tailwindcss@4.2.2: + resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} @@ -5759,6 +5985,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.37 + '@inquirer/confirm@5.1.21(@types/node@22.19.15)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.15) + '@inquirer/type': 3.0.10(@types/node@22.19.15) + optionalDependencies: + '@types/node': 22.19.15 + '@inquirer/core@10.3.2(@types/node@20.19.37)': dependencies: '@inquirer/ansi': 1.0.2 @@ -5772,6 +6005,19 @@ snapshots: optionalDependencies: '@types/node': 20.19.37 + '@inquirer/core@10.3.2(@types/node@22.19.15)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.19.15) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.19.15 + '@inquirer/external-editor@1.0.3(@types/node@22.19.15)': dependencies: chardet: 2.1.1 @@ -5785,6 +6031,10 @@ snapshots: optionalDependencies: '@types/node': 20.19.37 + '@inquirer/type@3.0.10(@types/node@22.19.15)': + optionalDependencies: + '@types/node': 22.19.15 + '@ioredis/commands@1.5.1': {} '@jridgewell/gen-mapping@0.3.13': @@ -6886,6 +7136,8 @@ snapshots: prompts: 2.4.2 smol-toml: 1.6.0 + '@rolldown/pluginutils@1.0.0-rc.7': {} + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true @@ -6995,42 +7247,88 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.2.1 + '@tailwindcss/node@4.2.2': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.20.0 + jiti: 2.6.1 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.2 + '@tailwindcss/oxide-android-arm64@4.2.1': optional: true + '@tailwindcss/oxide-android-arm64@4.2.2': + optional: true + '@tailwindcss/oxide-darwin-arm64@4.2.1': optional: true + '@tailwindcss/oxide-darwin-arm64@4.2.2': + optional: true + '@tailwindcss/oxide-darwin-x64@4.2.1': optional: true + '@tailwindcss/oxide-darwin-x64@4.2.2': + optional: true + '@tailwindcss/oxide-freebsd-x64@4.2.1': optional: true + '@tailwindcss/oxide-freebsd-x64@4.2.2': + optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + optional: true + '@tailwindcss/oxide-linux-x64-musl@4.2.1': optional: true + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + optional: true + '@tailwindcss/oxide-wasm32-wasi@4.2.1': optional: true + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + optional: true + '@tailwindcss/oxide@4.2.1': optionalDependencies: '@tailwindcss/oxide-android-arm64': 4.2.1 @@ -7046,6 +7344,21 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 + '@tailwindcss/oxide@4.2.2': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-x64': 4.2.2 + '@tailwindcss/oxide-freebsd-x64': 4.2.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-x64-musl': 4.2.2 + '@tailwindcss/oxide-wasm32-wasi': 4.2.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + '@tailwindcss/postcss@4.2.1': dependencies: '@alloc/quick-lru': 5.2.0 @@ -7054,6 +7367,13 @@ snapshots: postcss: 8.5.8 tailwindcss: 4.2.1 + '@tailwindcss/vite@4.2.2(@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))': + dependencies: + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 + tailwindcss: 4.2.2 + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' + '@tanstack/query-core@5.90.20': {} '@tanstack/react-query@5.90.21(react@19.2.4)': @@ -7143,6 +7463,13 @@ snapshots: '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260319.1 '@typescript/native-preview-win32-x64': 7.0.0-dev.20260319.1 + '@vitejs/plugin-react@6.0.1(@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(babel-plugin-react-compiler@1.0.0)': + dependencies: + '@rolldown/pluginutils': 1.0.0-rc.7 + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' + optionalDependencies: + babel-plugin-react-compiler: 1.0.0 + '@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)': dependencies: '@oxc-project/runtime': 0.115.0 @@ -7170,7 +7497,7 @@ snapshots: '@voidzero-dev/vite-plus-linux-x64-gnu@0.1.12': optional: true - '@voidzero-dev/vite-plus-test@0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)': + '@voidzero-dev/vite-plus-test@0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 @@ -7184,7 +7511,7 @@ snapshots: tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -8126,36 +8453,69 @@ snapshots: lightningcss-android-arm64@1.31.1: optional: true + lightningcss-android-arm64@1.32.0: + optional: true + lightningcss-darwin-arm64@1.31.1: optional: true + lightningcss-darwin-arm64@1.32.0: + optional: true + lightningcss-darwin-x64@1.31.1: optional: true + lightningcss-darwin-x64@1.32.0: + optional: true + lightningcss-freebsd-x64@1.31.1: optional: true + lightningcss-freebsd-x64@1.32.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.31.1: optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + lightningcss-linux-arm64-gnu@1.31.1: optional: true + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + lightningcss-linux-arm64-musl@1.31.1: optional: true + lightningcss-linux-arm64-musl@1.32.0: + optional: true + lightningcss-linux-x64-gnu@1.31.1: optional: true + lightningcss-linux-x64-gnu@1.32.0: + optional: true + lightningcss-linux-x64-musl@1.31.1: optional: true + lightningcss-linux-x64-musl@1.32.0: + optional: true + lightningcss-win32-arm64-msvc@1.31.1: optional: true + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + lightningcss-win32-x64-msvc@1.31.1: optional: true + lightningcss-win32-x64-msvc@1.32.0: + optional: true + lightningcss@1.31.1: dependencies: detect-libc: 2.1.2 @@ -8172,6 +8532,22 @@ snapshots: lightningcss-win32-arm64-msvc: 1.31.1 lightningcss-win32-x64-msvc: 1.31.1 + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lines-and-columns@1.2.4: {} locate-path@5.0.0: @@ -8197,6 +8573,10 @@ snapshots: dependencies: react: 19.2.3 + lucide-react@0.576.0(react@19.2.4): + dependencies: + react: 19.2.4 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -8297,6 +8677,31 @@ snapshots: transitivePeerDependencies: - '@types/node' + msw@2.12.10(@types/node@22.19.15)(typescript@5.9.3): + dependencies: + '@inquirer/confirm': 5.1.21(@types/node@22.19.15) + '@mswjs/interceptors': 0.41.3 + '@open-draft/deferred-promise': 2.2.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.13.1 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.10.1 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.4.4 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + multipasta@0.2.7: {} mute-stream@2.0.0: {} @@ -8666,6 +9071,11 @@ snapshots: react: 19.2.3 scheduler: 0.27.0 + react-dom@19.2.3(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + react-grab@0.1.28(@types/react@19.2.14)(react@19.2.3): dependencies: '@medv/finder': 4.0.2 @@ -8918,6 +9328,50 @@ snapshots: - supports-color - typescript + shadcn@3.8.5(@types/node@22.19.15)(typescript@5.9.3): + dependencies: + '@antfu/ni': 25.0.0 + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@dotenvx/dotenvx': 1.55.1 + '@modelcontextprotocol/sdk': 1.27.1(zod@3.25.76) + '@types/validate-npm-package-name': 4.0.2 + browserslist: 4.28.1 + commander: 14.0.3 + cosmiconfig: 9.0.1(typescript@5.9.3) + dedent: 1.7.2 + deepmerge: 4.3.1 + diff: 8.0.3 + execa: 9.6.1 + fast-glob: 3.3.3 + fs-extra: 11.3.4 + fuzzysort: 3.1.0 + https-proxy-agent: 7.0.6 + kleur: 4.1.5 + msw: 2.12.10(@types/node@22.19.15)(typescript@5.9.3) + node-fetch: 3.3.2 + open: 11.0.0 + ora: 8.2.0 + postcss: 8.5.8 + postcss-selector-parser: 7.1.1 + prompts: 2.4.2 + recast: 0.23.11 + stringify-object: 5.0.0 + tailwind-merge: 3.5.0 + ts-morph: 26.0.0 + tsconfig-paths: 4.2.0 + validate-npm-package-name: 7.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@types/node' + - babel-plugin-macros + - supports-color + - typescript + sharp@0.34.5: dependencies: '@img/colour': 1.1.0 @@ -9105,6 +9559,8 @@ snapshots: tailwindcss@4.2.1: {} + tailwindcss@4.2.2: {} + tapable@2.3.0: {} term-size@2.2.1: {} @@ -9263,11 +9719,11 @@ snapshots: vary@1.1.2: {} - vite-plus@0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2): + vite-plus@0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2): dependencies: '@oxc-project/types': 0.115.0 '@voidzero-dev/vite-plus-core': 0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) - '@voidzero-dev/vite-plus-test': 0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2) + '@voidzero-dev/vite-plus-test': 0.1.12(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2) cac: 6.7.14 cross-spawn: 7.0.6 oxfmt: 0.40.0 @@ -9309,7 +9765,7 @@ snapshots: - vite - yaml - vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.3) @@ -9321,7 +9777,7 @@ snapshots: '@types/node': 22.19.15 fsevents: 2.3.3 jiti: 2.6.1 - lightningcss: 1.31.1 + lightningcss: 1.32.0 tsx: 4.21.0 yaml: 2.8.2 From ecd1d1e203f595b62b8ffefa5ad0b0f6dfb911b3 Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Mon, 23 Mar 2026 20:57:20 -0700 Subject: [PATCH 04/10] fix --- packages/recorder/package.json | 2 +- packages/recorder/viewer/components.json | 2 +- packages/recorder/viewer/src/app.tsx | 60 ++++++++++++------------ packages/recorder/viewer/vite.config.ts | 5 +- pnpm-lock.yaml | 2 +- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/recorder/package.json b/packages/recorder/package.json index bb1a5842..e10c47b3 100644 --- a/packages/recorder/package.json +++ b/packages/recorder/package.json @@ -49,7 +49,7 @@ "clsx": "^2.1.1", "lucide-react": "^0.576.0", "react": "^19.2.4", - "react-dom": "19.2.3", + "react-dom": "^19.2.3", "shadcn": "^3.8.5", "tailwind-merge": "^3.5.0", "tailwindcss": "^4.2.1", diff --git a/packages/recorder/viewer/components.json b/packages/recorder/viewer/components.json index d111985e..9655ae1d 100644 --- a/packages/recorder/viewer/components.json +++ b/packages/recorder/viewer/components.json @@ -1,6 +1,6 @@ { "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", + "style": "radix-nova", "rsc": false, "tsx": true, "tailwind": { diff --git a/packages/recorder/viewer/src/app.tsx b/packages/recorder/viewer/src/app.tsx index 4a801c17..cd2ac2b2 100644 --- a/packages/recorder/viewer/src/app.tsx +++ b/packages/recorder/viewer/src/app.tsx @@ -37,6 +37,35 @@ export const App = () => { }, []); useMountEffect(() => { + const eventSource = new EventSource("/events"); + + eventSource.addEventListener("replay", (message) => { + try { + const events: eventWithTime[] = JSON.parse(message.data); + for (const event of events) { + eventsRef.current.push(event); + if (playerRef.current) playerRef.current.getReplayer().addEvent(event); + } + if (!playerRef.current && eventsRef.current.length >= 2) { + initPlayer(eventsRef.current); + } + } catch { + /* ignore malformed events */ + } + }); + + eventSource.addEventListener("steps", (message) => { + try { + setRunState(JSON.parse(message.data)); + } catch { + /* ignore malformed steps */ + } + }); + + eventSource.onerror = () => { + setStatus("Connection lost. Retrying..."); + }; + const bootstrap = async () => { const latestResponse = await fetch("/latest.json"); if (latestResponse.ok) { @@ -49,38 +78,11 @@ export const App = () => { const state = await stepsResponse.json(); if (state?.steps) setRunState(state); } - - const eventSource = new EventSource("/events"); - - eventSource.addEventListener("replay", (message) => { - try { - const events: eventWithTime[] = JSON.parse(message.data); - for (const event of events) { - eventsRef.current.push(event); - if (playerRef.current) playerRef.current.getReplayer().addEvent(event); - } - if (!playerRef.current && eventsRef.current.length >= 2) { - initPlayer(eventsRef.current); - } - } catch { - /* ignore malformed events */ - } - }); - - eventSource.addEventListener("steps", (message) => { - try { - setRunState(JSON.parse(message.data)); - } catch { - /* ignore malformed steps */ - } - }); - - eventSource.onerror = () => { - setStatus("Connection lost. Retrying..."); - }; }; bootstrap(); + + return () => eventSource.close(); }); return ( diff --git a/packages/recorder/viewer/vite.config.ts b/packages/recorder/viewer/vite.config.ts index ab008a09..5559a651 100644 --- a/packages/recorder/viewer/vite.config.ts +++ b/packages/recorder/viewer/vite.config.ts @@ -1,4 +1,5 @@ -import { resolve } from "node:path"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; import tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; @@ -7,7 +8,7 @@ export default defineConfig({ plugins: [tailwindcss(), react()], resolve: { alias: { - "@": resolve(import.meta.dirname, "src"), + "@": resolve(dirname(fileURLToPath(import.meta.url)), "src"), }, }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8a3da867..0807c1f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -325,7 +325,7 @@ importers: specifier: ^19.2.4 version: 19.2.4 react-dom: - specifier: 19.2.3 + specifier: ^19.2.3 version: 19.2.3(react@19.2.4) shadcn: specifier: ^3.8.5 From f95253ba7334e4cf9a9412ee7902ae51cd9548bb Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Mon, 23 Mar 2026 22:59:53 -0700 Subject: [PATCH 05/10] fix --- packages/recorder/package.json | 2 +- packages/recorder/viewer/src/app.tsx | 72 ++++++++++++++++------------ pnpm-lock.yaml | 11 +++-- 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/packages/recorder/package.json b/packages/recorder/package.json index e10c47b3..aaa3eaef 100644 --- a/packages/recorder/package.json +++ b/packages/recorder/package.json @@ -49,7 +49,7 @@ "clsx": "^2.1.1", "lucide-react": "^0.576.0", "react": "^19.2.4", - "react-dom": "^19.2.3", + "react-dom": "^19.2.4", "shadcn": "^3.8.5", "tailwind-merge": "^3.5.0", "tailwindcss": "^4.2.1", diff --git a/packages/recorder/viewer/src/app.tsx b/packages/recorder/viewer/src/app.tsx index cd2ac2b2..a2bb2ee7 100644 --- a/packages/recorder/viewer/src/app.tsx +++ b/packages/recorder/viewer/src/app.tsx @@ -37,52 +37,64 @@ export const App = () => { }, []); useMountEffect(() => { - const eventSource = new EventSource("/events"); - - eventSource.addEventListener("replay", (message) => { - try { - const events: eventWithTime[] = JSON.parse(message.data); - for (const event of events) { - eventsRef.current.push(event); - if (playerRef.current) playerRef.current.getReplayer().addEvent(event); - } - if (!playerRef.current && eventsRef.current.length >= 2) { - initPlayer(eventsRef.current); + let eventSource: EventSource | undefined; + + const isJsonResponse = (response: Response) => + response.ok && Boolean(response.headers.get("content-type")?.includes("application/json")); + + const connectSse = () => { + eventSource = new EventSource("/events"); + + eventSource.addEventListener("replay", (message) => { + try { + const events: eventWithTime[] = JSON.parse(message.data); + for (const event of events) { + eventsRef.current.push(event); + if (playerRef.current) playerRef.current.getReplayer().addEvent(event); + } + if (!playerRef.current && eventsRef.current.length >= 2) { + initPlayer(eventsRef.current); + } + } catch { + /* ignore malformed events */ } - } catch { - /* ignore malformed events */ - } - }); + }); - eventSource.addEventListener("steps", (message) => { - try { - setRunState(JSON.parse(message.data)); - } catch { - /* ignore malformed steps */ - } - }); + eventSource.addEventListener("steps", (message) => { + try { + setRunState(JSON.parse(message.data)); + } catch { + /* ignore malformed steps */ + } + }); - eventSource.onerror = () => { - setStatus("Connection lost. Retrying..."); + eventSource.onerror = () => { + setStatus("Connection lost. Retrying..."); + }; }; const bootstrap = async () => { - const latestResponse = await fetch("/latest.json"); - if (latestResponse.ok) { - eventsRef.current = await latestResponse.json(); - if (eventsRef.current.length >= 2) initPlayer(eventsRef.current); + const probeResponse = await fetch("/latest.json"); + if (!isJsonResponse(probeResponse)) { + setStatus("Waiting for test run..."); + return; } + eventsRef.current = await probeResponse.json(); + if (eventsRef.current.length >= 2) initPlayer(eventsRef.current); + const stepsResponse = await fetch("/steps"); - if (stepsResponse.ok) { + if (isJsonResponse(stepsResponse)) { const state = await stepsResponse.json(); if (state?.steps) setRunState(state); } + + connectSse(); }; bootstrap(); - return () => eventSource.close(); + return () => eventSource?.close(); }); return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0807c1f9..697ed29e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -325,8 +325,8 @@ importers: specifier: ^19.2.4 version: 19.2.4 react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.4) + specifier: ^19.2.4 + version: 19.2.4(react@19.2.4) shadcn: specifier: ^3.8.5 version: 3.8.5(@types/node@22.19.15)(typescript@5.9.3) @@ -4563,6 +4563,11 @@ packages: peerDependencies: react: ^19.2.3 + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + react-grab@0.1.28: resolution: {integrity: sha512-u3fvu7a7ejHhuWKzf/N6sFavV04vcqwtbcqyxwNPtvd3ts9KSVerpHp1ZH0/XVKTsh3MZz1u1jyIt0KYC9L/Rg==} hasBin: true @@ -9071,7 +9076,7 @@ snapshots: react: 19.2.3 scheduler: 0.27.0 - react-dom@19.2.3(react@19.2.4): + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 scheduler: 0.27.0 From c656fd0e92b485cc0e7501279e9b288e0aa9826b Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Tue, 24 Mar 2026 00:13:12 -0700 Subject: [PATCH 06/10] feat: file-based replay log replaces SSE live view Replace the SSE-based live view server with a simpler file-based approach. The MCP session now writes rrweb events incrementally to .testie/browser-flow.ndjson and run state to .testie/run-state.json. The viewer polls these files instead of maintaining an SSE connection. Co-Authored-By: Claude Opus 4.6 --- apps/cli/src/index.tsx | 16 +- packages/browser/src/mcp/mcp-session.ts | 114 +++++------ packages/recorder/src/constants.ts | 4 + packages/recorder/src/index.ts | 7 +- packages/recorder/src/live-view-server.ts | 223 +++------------------- packages/recorder/src/replay-log.ts | 27 +++ packages/recorder/viewer/src/app.tsx | 105 +++++----- packages/recorder/viewer/vite.config.ts | 40 +++- packages/supervisor/src/constants.ts | 1 - packages/supervisor/src/index.ts | 1 + 10 files changed, 226 insertions(+), 312 deletions(-) create mode 100644 packages/recorder/src/replay-log.ts diff --git a/apps/cli/src/index.tsx b/apps/cli/src/index.tsx index c5b84772..cc69510f 100644 --- a/apps/cli/src/index.tsx +++ b/apps/cli/src/index.tsx @@ -1,3 +1,4 @@ +import { join } from "node:path"; import { Effect, Option } from "effect"; import { Command } from "commander"; import { render } from "ink"; @@ -6,7 +7,14 @@ import { App } from "./components/app.js"; import { ALT_SCREEN_OFF, ALT_SCREEN_ON, VERSION } from "./constants.js"; import { ThemeProvider } from "./components/theme-context.js"; import { loadThemeName } from "./utils/load-theme.js"; -import { ChangesFor, Git, TestPlanDraft, DraftId } from "@expect/supervisor"; +import { + ChangesFor, + Git, + TestPlanDraft, + DraftId, + EXPECT_STATE_DIR, + REPLAY_FILE_NAME, +} from "@expect/supervisor"; import { runHeadless } from "./utils/run-test.js"; import type { AgentBackend } from "@expect/agent"; import { useNavigationStore, Screen } from "./stores/use-navigation.js"; @@ -17,6 +25,12 @@ import { setInkInstance } from "./utils/clear-ink-display.js"; import { RegistryProvider } from "@effect/atom-react"; import { agentProviderAtom } from "./data/runtime.js"; +process.env.BROWSER_TESTER_REPLAY_OUTPUT_PATH = join( + process.cwd(), + EXPECT_STATE_DIR, + REPLAY_FILE_NAME, +); + const DEFAULT_SKIP_PLANNING = true; const DEFAULT_INSTRUCTION = diff --git a/packages/browser/src/mcp/mcp-session.ts b/packages/browser/src/mcp/mcp-session.ts index 09640b41..ad5b2f38 100644 --- a/packages/browser/src/mcp/mcp-session.ts +++ b/packages/browser/src/mcp/mcp-session.ts @@ -4,12 +4,14 @@ import { Config, Effect, Fiber, Layer, Option, Ref, Schedule, ServiceMap } from import { FileSystem } from "effect/FileSystem"; import { collectAllEvents, + createReplayLog, evaluateRecorderRuntime, buildReplayViewerHtml, startLiveViewServer, EVENT_COLLECT_INTERVAL_MS, type eventWithTime, type LiveViewHandle, + type ReplayLog, type ViewerRunState, } from "@browser-tester/recorder"; import { Browser } from "../browser"; @@ -39,6 +41,7 @@ export interface BrowserSessionData { readonly consoleMessages: ConsoleEntry[]; readonly networkRequests: NetworkEntry[]; readonly replayOutputPath: string | undefined; + readonly replayLog: ReplayLog | undefined; readonly accumulatedReplayEvents: eventWithTime[]; readonly trackedPages: Set; lastSnapshot: SnapshotResult | undefined; @@ -128,10 +131,8 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe const pushStepEvent = Effect.fn("McpSession.pushStepEvent")(function* (state: ViewerRunState) { yield* Ref.set(latestRunStateRef, state); - const liveView = yield* Ref.get(liveViewRef); - if (liveView) { - liveView.pushRunState(state); - } + const session = yield* Ref.get(sessionRef); + session?.replayLog?.writeRunState(state); }); const open = Effect.fn("McpSession.open")(function* (url: string, options: OpenOptions = {}) { @@ -143,13 +144,15 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe waitUntil: options.waitUntil, }); + const outputPath = Option.getOrUndefined(replayOutputPath); const sessionData: BrowserSessionData = { browser: pageResult.browser, context: pageResult.context, page: pageResult.page, consoleMessages: [], networkRequests: [], - replayOutputPath: Option.getOrUndefined(replayOutputPath), + replayOutputPath: outputPath, + replayLog: outputPath ? createReplayLog(outputPath) : undefined, accumulatedReplayEvents: [], trackedPages: new Set(), lastSnapshot: undefined, @@ -161,15 +164,35 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe Effect.catchCause((cause) => Effect.logDebug("rrweb recording failed to start", { cause })), ); + const pollPage = Effect.sync(() => Ref.getUnsafe(sessionRef)?.page).pipe( + Effect.flatMap((page) => { + if (!page || page.isClosed()) return Effect.void; + return evaluateRecorderRuntime(page, "getEvents").pipe( + Effect.tap((events) => + Effect.sync(() => { + if (Array.isArray(events) && events.length > 0) { + const session = Ref.getUnsafe(sessionRef); + session?.accumulatedReplayEvents.push(...events); + session?.replayLog?.appendEvents(events); + } + }), + ), + Effect.catchCause((cause) => + Effect.logDebug("Replay event collection failed", { cause }), + ), + ); + }), + ); + + const fiber = yield* pollPage.pipe( + Effect.repeat(Schedule.spaced(EVENT_COLLECT_INTERVAL_MS)), + Effect.forkDetach, + ); + yield* Ref.set(pollingFiberRef, fiber); + const existingLiveView = yield* Ref.get(liveViewRef); if (Option.isSome(liveViewUrl) && !existingLiveView) { - const handle = yield* startLiveViewServer({ - liveViewUrl: liveViewUrl.value, - getPage: () => Ref.getUnsafe(sessionRef)?.page, - onEventsCollected: (events) => { - Ref.getUnsafe(sessionRef)?.accumulatedReplayEvents.push(...events); - }, - }).pipe( + const handle = yield* startLiveViewServer(liveViewUrl.value).pipe( Effect.catchCause((cause) => Effect.logDebug("Live view server failed to start", { cause }).pipe( Effect.as(undefined), @@ -181,33 +204,6 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe } } - const hasLiveView = Boolean(yield* Ref.get(liveViewRef)); - if (!hasLiveView) { - const pollPage = Effect.sync(() => Ref.getUnsafe(sessionRef)?.page).pipe( - Effect.flatMap((page) => { - if (!page || page.isClosed()) return Effect.void; - return evaluateRecorderRuntime(page, "getEvents").pipe( - Effect.tap((events) => - Effect.sync(() => { - if (Array.isArray(events) && events.length > 0) { - Ref.getUnsafe(sessionRef)?.accumulatedReplayEvents.push(...events); - } - }), - ), - Effect.catchCause((cause) => - Effect.logDebug("Replay event collection failed", { cause }), - ), - ); - }), - ); - - const fiber = yield* pollPage.pipe( - Effect.repeat(Schedule.spaced(EVENT_COLLECT_INTERVAL_MS)), - Effect.forkDetach, - ); - yield* Ref.set(pollingFiberRef, fiber); - } - const injectedCookieCount = yield* Effect.tryPromise(() => pageResult.context.cookies()).pipe( Effect.map((cookies) => cookies.length), Effect.catchCause((cause) => @@ -273,29 +269,33 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe ); if (finalEvents.length > 0) { activeSession.accumulatedReplayEvents.push(...finalEvents); + activeSession.replayLog?.appendEvents(finalEvents); } } const resolvedReplayOutputPath = activeSession.replayOutputPath; if (resolvedReplayOutputPath && activeSession.accumulatedReplayEvents.length > 0) { - const ndjson = - activeSession.accumulatedReplayEvents.map((event) => JSON.stringify(event)).join("\n") + - "\n"; - - yield* fileSystem - .makeDirectory(dirname(resolvedReplayOutputPath), { recursive: true }) - .pipe( - Effect.catchCause((cause) => - Effect.logDebug("Failed to create replay output directory", { cause }), - ), - ); - yield* fileSystem - .writeFileString(resolvedReplayOutputPath, ndjson) - .pipe( - Effect.catchCause((cause) => - Effect.logDebug("Failed to write replay file", { cause }), - ), - ); + if (!activeSession.replayLog) { + const ndjson = + activeSession.accumulatedReplayEvents + .map((event) => JSON.stringify(event)) + .join("\n") + "\n"; + + yield* fileSystem + .makeDirectory(dirname(resolvedReplayOutputPath), { recursive: true }) + .pipe( + Effect.catchCause((cause) => + Effect.logDebug("Failed to create replay output directory", { cause }), + ), + ); + yield* fileSystem + .writeFileString(resolvedReplayOutputPath, ndjson) + .pipe( + Effect.catchCause((cause) => + Effect.logDebug("Failed to write replay file", { cause }), + ), + ); + } replaySessionPath = resolvedReplayOutputPath; const runState = yield* Ref.get(latestRunStateRef); diff --git a/packages/recorder/src/constants.ts b/packages/recorder/src/constants.ts index 4e665140..f1518f87 100644 --- a/packages/recorder/src/constants.ts +++ b/packages/recorder/src/constants.ts @@ -2,3 +2,7 @@ export const EVENT_COLLECT_INTERVAL_MS = 500; export const REPLAY_PLAYER_WIDTH_PX = 960; export const REPLAY_PLAYER_HEIGHT_PX = 540; + +export const RUN_STATE_FILE_NAME = "run-state.json"; +export const EXPECT_STATE_DIR = ".expect"; +export const REPLAY_FILE_NAME = "browser-flow.ndjson"; diff --git a/packages/recorder/src/index.ts b/packages/recorder/src/index.ts index 5b2151fb..236bb90d 100644 --- a/packages/recorder/src/index.ts +++ b/packages/recorder/src/index.ts @@ -1,14 +1,19 @@ export { collectEvents, collectAllEvents, loadSession } from "./recorder"; export { buildReplayViewerHtml } from "./replay-viewer"; export { startLiveViewServer } from "./live-view-server"; -export type { LiveViewHandle, StartLiveViewServerOptions } from "./live-view-server"; +export type { LiveViewHandle } from "./live-view-server"; +export { createReplayLog } from "./replay-log"; +export type { ReplayLog } from "./replay-log"; export { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; export { RecorderInjectionError, SessionLoadError } from "./errors"; export type { CollectResult } from "./types"; export type { ViewerRunState, ViewerStepEvent } from "./viewer-events"; export { EVENT_COLLECT_INTERVAL_MS, + REPLAY_FILE_NAME, REPLAY_PLAYER_WIDTH_PX, REPLAY_PLAYER_HEIGHT_PX, + RUN_STATE_FILE_NAME, + EXPECT_STATE_DIR, } from "./constants"; export type { eventWithTime } from "@rrweb/types"; diff --git a/packages/recorder/src/live-view-server.ts b/packages/recorder/src/live-view-server.ts index 28529d8d..aef397e4 100644 --- a/packages/recorder/src/live-view-server.ts +++ b/packages/recorder/src/live-view-server.ts @@ -1,213 +1,48 @@ -import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http"; -import type { Page } from "playwright"; -import type { eventWithTime } from "@rrweb/types"; -import { Effect, Fiber, Predicate, PubSub, Schedule, Stream } from "effect"; -import { EVENT_COLLECT_INTERVAL_MS } from "./constants"; -import { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; -import { buildReplayViewerHtml } from "./replay-viewer"; -import type { ViewerRunState } from "./viewer-events"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { Effect } from "effect"; -const isViewerRunState = (value: unknown): value is ViewerRunState => - Predicate.isObject(value) && - "status" in value && - "steps" in value && - Array.isArray((value as Record).steps); +const VIEWER_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../viewer"); export interface LiveViewHandle { readonly url: string; - readonly pushRunState: (state: ViewerRunState) => void; - readonly getLatestRunState: () => ViewerRunState | undefined; readonly close: Effect.Effect; } -export interface StartLiveViewServerOptions { - readonly liveViewUrl: string; - readonly getPage: () => Page | undefined; - readonly onEventsCollected: (events: eventWithTime[]) => void; -} - -type SseClient = ServerResponse; - -const NO_CACHE_HEADERS = { "Cache-Control": "no-store" } as const; - -const listenServer = (server: Server, host: string, port: number) => - Effect.callback((resume) => { - const onError = (error: Error) => resume(Effect.fail(error)); - server.once("error", onError); - server.listen({ host, port }, () => { - server.off("error", onError); - resume(Effect.void); - }); - }); - -const closeServer = (server: Server) => - Effect.callback((resume) => { - server.close(() => resume(Effect.void)); - }); - export const startLiveViewServer = Effect.fn("LiveViewServer.start")(function* ( - options: StartLiveViewServerOptions, + liveViewUrl: string, ) { - const parsedUrl = new URL(options.liveViewUrl); - const sseClients = new Set(); - const accumulatedReplayEvents: eventWithTime[] = []; - let latestRunState: ViewerRunState | undefined; - - const stepsPubSub = yield* PubSub.unbounded(); + const parsedUrl = new URL(liveViewUrl); - const viewerHtml = buildReplayViewerHtml({ - title: "Expect Live View", - eventsSource: "sse", + const { createServer: createViteServer } = yield* Effect.tryPromise({ + try: () => import("vite"), + catch: (cause) => new Error(`Failed to load vite: ${cause}`), }); - const broadcastSse = (eventType: string, data: string): void => { - const message = `event: ${eventType}\ndata: ${data}\n\n`; - for (const client of sseClients) { - if (client.destroyed) { - sseClients.delete(client); - continue; - } - try { - client.write(message); - } catch { - sseClients.delete(client); - client.end(); - } - } - }; - - const broadcastReplayEvents = (events: eventWithTime[]): void => { - if (events.length === 0) return; - accumulatedReplayEvents.push(...events); - options.onEventsCollected(events); - broadcastSse("replay", JSON.stringify(events)); - }; - - const broadcastRunState = (state: ViewerRunState): void => { - latestRunState = state; - broadcastSse("steps", JSON.stringify(state)); - }; - - const handleSseRequest = (request: IncomingMessage, response: SseClient): void => { - response.writeHead(200, { - "Content-Type": "text/event-stream", - Connection: "keep-alive", - ...NO_CACHE_HEADERS, - }); - response.flushHeaders(); - sseClients.add(response); - request.on("close", () => sseClients.delete(response)); - }; - - const handleStepsPost = (request: IncomingMessage, response: SseClient): void => { - const chunks: Buffer[] = []; - request.on("data", (chunk: Buffer) => chunks.push(chunk)); - request.on("end", () => { - try { - const body = Buffer.concat(chunks).toString("utf-8"); - const parsed: unknown = JSON.parse(body); - if (!isViewerRunState(parsed)) { - response.writeHead(400, { "Content-Type": "text/plain", ...NO_CACHE_HEADERS }); - response.end("Invalid step state: missing status or steps"); - return; - } - broadcastRunState(parsed); - response.writeHead(204, NO_CACHE_HEADERS); - response.end(); - } catch { - response.writeHead(400, { "Content-Type": "text/plain", ...NO_CACHE_HEADERS }); - response.end("Invalid JSON"); - } - }); - }; - - const routeRequest = (request: IncomingMessage, response: SseClient): void => { - const pathname = new URL(request.url ?? "/", parsedUrl).pathname; - - if (pathname === "/") { - response.writeHead(200, { "Content-Type": "text/html; charset=utf-8", ...NO_CACHE_HEADERS }); - response.end(viewerHtml); - return; - } - - if (pathname === "/events") { - handleSseRequest(request, response); - return; - } - - if (pathname === "/latest.json") { - const body = JSON.stringify(accumulatedReplayEvents); - response.writeHead(200, { - "Content-Type": "application/json", - "Content-Length": Buffer.byteLength(body), - ...NO_CACHE_HEADERS, - }); - response.end(body); - return; - } - - if (pathname === "/steps") { - if (request.method === "POST") { - handleStepsPost(request, response); - return; - } - const body = JSON.stringify(latestRunState ?? { title: "", status: "running", steps: [] }); - response.writeHead(200, { - "Content-Type": "application/json", - "Content-Length": Buffer.byteLength(body), - ...NO_CACHE_HEADERS, - }); - response.end(body); - return; - } - - response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8", ...NO_CACHE_HEADERS }); - response.end("Not found"); - }; - - const server = createServer(routeRequest); - - yield* listenServer(server, parsedUrl.hostname, Number(parsedUrl.port)); - - const pollPage = Effect.sync(() => options.getPage()).pipe( - Effect.flatMap((page) => { - if (!page || page.isClosed()) return Effect.void; - return evaluateRecorderRuntime(page, "getEvents").pipe( - Effect.tap((events) => - Effect.sync(() => { - if (Array.isArray(events) && events.length > 0) { - broadcastReplayEvents(events); - } - }), - ), - Effect.catchCause((cause) => Effect.logDebug("Replay event collection failed", { cause })), - ); - }), - ); - - const replayPollFiber = yield* pollPage.pipe( - Effect.repeat(Schedule.spaced(EVENT_COLLECT_INTERVAL_MS)), - Effect.forkDetach, - ); + const viteServer = yield* Effect.tryPromise({ + try: () => + createViteServer({ + root: VIEWER_ROOT, + server: { + host: parsedUrl.hostname, + port: Number(parsedUrl.port), + strictPort: true, + }, + logLevel: "silent", + }), + catch: (cause) => new Error(`Failed to create Vite server: ${cause}`), + }); - const stepsBroadcastFiber = yield* Stream.fromPubSub(stepsPubSub).pipe( - Stream.tap((state) => Effect.sync(() => broadcastRunState(state))), - Stream.runDrain, - Effect.forkDetach, - ); + yield* Effect.tryPromise({ + try: () => viteServer.listen(), + catch: (cause) => new Error(`Failed to start Vite server: ${cause}`), + }); return { url: parsedUrl.toString(), - pushRunState: (state: ViewerRunState) => { - PubSub.publishUnsafe(stepsPubSub, state); - }, - getLatestRunState: () => latestRunState, - close: Effect.gen(function* () { - yield* Fiber.interrupt(replayPollFiber); - yield* Fiber.interrupt(stepsBroadcastFiber); - for (const client of sseClients) client.end(); - sseClients.clear(); - yield* closeServer(server); + close: Effect.tryPromise({ + try: () => viteServer.close(), + catch: () => new Error("Failed to close Vite server"), }), } satisfies LiveViewHandle; }); diff --git a/packages/recorder/src/replay-log.ts b/packages/recorder/src/replay-log.ts new file mode 100644 index 00000000..ca4f1c3e --- /dev/null +++ b/packages/recorder/src/replay-log.ts @@ -0,0 +1,27 @@ +import { appendFileSync, mkdirSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import type { eventWithTime } from "@rrweb/types"; +import { RUN_STATE_FILE_NAME } from "./constants"; +import type { ViewerRunState } from "./viewer-events"; + +export interface ReplayLog { + readonly appendEvents: (events: readonly eventWithTime[]) => void; + readonly writeRunState: (state: ViewerRunState) => void; +} + +export const createReplayLog = (ndjsonPath: string): ReplayLog => { + const directory = dirname(ndjsonPath); + mkdirSync(directory, { recursive: true }); + writeFileSync(ndjsonPath, ""); + + return { + appendEvents: (events) => { + if (events.length === 0) return; + const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n"; + appendFileSync(ndjsonPath, lines); + }, + writeRunState: (state) => { + writeFileSync(join(directory, RUN_STATE_FILE_NAME), JSON.stringify(state)); + }, + }; +}; diff --git a/packages/recorder/viewer/src/app.tsx b/packages/recorder/viewer/src/app.tsx index a2bb2ee7..2e9c26aa 100644 --- a/packages/recorder/viewer/src/app.tsx +++ b/packages/recorder/viewer/src/app.tsx @@ -2,17 +2,28 @@ import { useCallback, useRef, useState } from "react"; import rrwebPlayer from "rrweb-player"; import "rrweb-player/dist/style.css"; import type { eventWithTime } from "@rrweb/types"; -import { REPLAY_PLAYER_HEIGHT_PX, REPLAY_PLAYER_WIDTH_PX } from "../../src/constants"; +import { + EVENT_COLLECT_INTERVAL_MS, + REPLAY_FILE_NAME, + REPLAY_PLAYER_HEIGHT_PX, + REPLAY_PLAYER_WIDTH_PX, + RUN_STATE_FILE_NAME, + EXPECT_STATE_DIR, +} from "../../src/constants"; import type { ViewerRunState } from "../../src/viewer-events"; import { useMountEffect } from "./hooks/use-mount-effect"; import { StepsPanel } from "./steps-panel"; +const REPLAY_URL = `/${EXPECT_STATE_DIR}/${REPLAY_FILE_NAME}`; +const RUN_STATE_URL = `/${EXPECT_STATE_DIR}/${RUN_STATE_FILE_NAME}`; + export const App = () => { const [runState, setRunState] = useState(); - const [status, setStatus] = useState("Loading replay\u2026"); + const [status, setStatus] = useState("Waiting for test run\u2026"); const containerRef = useRef(null); const playerRef = useRef(); const eventsRef = useRef([]); + const eventCountRef = useRef(0); const initPlayer = useCallback((events: eventWithTime[]) => { if (playerRef.current) { @@ -29,72 +40,54 @@ export const App = () => { width: REPLAY_PLAYER_WIDTH_PX, height: REPLAY_PLAYER_HEIGHT_PX, autoPlay: true, - showController: false, - liveMode: true, + showController: true, }, }); - playerRef.current.getReplayer().startLive(); }, []); useMountEffect(() => { - let eventSource: EventSource | undefined; - - const isJsonResponse = (response: Response) => - response.ok && Boolean(response.headers.get("content-type")?.includes("application/json")); - - const connectSse = () => { - eventSource = new EventSource("/events"); - - eventSource.addEventListener("replay", (message) => { - try { - const events: eventWithTime[] = JSON.parse(message.data); - for (const event of events) { - eventsRef.current.push(event); - if (playerRef.current) playerRef.current.getReplayer().addEvent(event); + const poll = async () => { + try { + const replayResponse = await fetch(REPLAY_URL); + if (replayResponse.ok) { + const text = await replayResponse.text(); + if (text.trim()) { + const lines = text.trim().split("\n"); + if (lines.length > eventCountRef.current) { + const newLines = lines.slice(eventCountRef.current); + for (const line of newLines) { + const event: eventWithTime = JSON.parse(line); + eventsRef.current.push(event); + if (playerRef.current) { + playerRef.current.getReplayer().addEvent(event); + } + } + eventCountRef.current = lines.length; + if (!playerRef.current && eventsRef.current.length >= 2) { + initPlayer(eventsRef.current); + } + setStatus(""); + } } - if (!playerRef.current && eventsRef.current.length >= 2) { - initPlayer(eventsRef.current); - } - } catch { - /* ignore malformed events */ - } - }); - - eventSource.addEventListener("steps", (message) => { - try { - setRunState(JSON.parse(message.data)); - } catch { - /* ignore malformed steps */ } - }); - - eventSource.onerror = () => { - setStatus("Connection lost. Retrying..."); - }; - }; - - const bootstrap = async () => { - const probeResponse = await fetch("/latest.json"); - if (!isJsonResponse(probeResponse)) { - setStatus("Waiting for test run..."); - return; + } catch { + /* file not available yet */ } - eventsRef.current = await probeResponse.json(); - if (eventsRef.current.length >= 2) initPlayer(eventsRef.current); - - const stepsResponse = await fetch("/steps"); - if (isJsonResponse(stepsResponse)) { - const state = await stepsResponse.json(); - if (state?.steps) setRunState(state); + try { + const stateResponse = await fetch(RUN_STATE_URL); + if (stateResponse.ok) { + const state: ViewerRunState = await stateResponse.json(); + if (state?.steps) setRunState(state); + } + } catch { + /* run state not available yet */ } - - connectSse(); }; - bootstrap(); - - return () => eventSource?.close(); + poll(); + const interval = setInterval(poll, EVENT_COLLECT_INTERVAL_MS); + return () => clearInterval(interval); }); return ( diff --git a/packages/recorder/viewer/vite.config.ts b/packages/recorder/viewer/vite.config.ts index 5559a651..9e01f1e3 100644 --- a/packages/recorder/viewer/vite.config.ts +++ b/packages/recorder/viewer/vite.config.ts @@ -1,11 +1,47 @@ +import { readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; +import { defineConfig, type Plugin } from "vite"; +import { EXPECT_STATE_DIR } from "../src/constants"; + +const CONTENT_TYPES: Record = { + ".json": "application/json", + ".ndjson": "application/x-ndjson", +}; + +const testieServePlugin = (): Plugin => ({ + name: "testie-serve", + configureServer(server) { + const testieDir = resolve(process.cwd(), EXPECT_STATE_DIR); + server.middlewares.use((request, response, next) => { + const pathname = new URL(request.url ?? "/", "http://localhost").pathname; + const prefix = `/${EXPECT_STATE_DIR}/`; + if (!pathname.startsWith(prefix)) return next(); + + const relativePath = pathname.slice(prefix.length); + const filePath = resolve(testieDir, relativePath); + if (!filePath.startsWith(testieDir)) return next(); + + try { + const content = readFileSync(filePath); + const extension = relativePath.slice(relativePath.lastIndexOf(".")); + response.writeHead(200, { + "Content-Type": CONTENT_TYPES[extension] ?? "application/octet-stream", + "Cache-Control": "no-store", + }); + response.end(content); + } catch { + response.writeHead(404); + response.end(); + } + }); + }, +}); export default defineConfig({ - plugins: [tailwindcss(), react()], + plugins: [testieServePlugin(), tailwindcss(), react()], resolve: { alias: { "@": resolve(dirname(fileURLToPath(import.meta.url)), "src"), diff --git a/packages/supervisor/src/constants.ts b/packages/supervisor/src/constants.ts index 78d387ae..9166c4fc 100644 --- a/packages/supervisor/src/constants.ts +++ b/packages/supervisor/src/constants.ts @@ -36,5 +36,4 @@ export const STEP_ID_PAD_LENGTH = 2; export const PLANNER_MAX_STEP_COUNT = 8; export const EXPECT_STATE_DIR = ".expect"; export const TESTED_FINGERPRINT_FILE = "last-tested"; -export const RUN_DIRECTORY_PREFIX = "expect-run-"; export const REPLAY_FILE_NAME = "browser-flow.ndjson"; diff --git a/packages/supervisor/src/index.ts b/packages/supervisor/src/index.ts index 46a4f34a..3fc713ad 100644 --- a/packages/supervisor/src/index.ts +++ b/packages/supervisor/src/index.ts @@ -2,6 +2,7 @@ export { Updates } from "./updates.js"; export { Planner, PlanningError } from "./planner.js"; export { Executor, ExecutionError } from "./executor.js"; export { Reporter } from "./reporter.js"; +export { EXPECT_STATE_DIR, REPLAY_FILE_NAME } from "./constants.js"; export { AgentProvider, type ChangedFile, From 17fa1f78d18f0063dd6daddf064d978a4cbbd676 Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Tue, 24 Mar 2026 02:59:24 -0700 Subject: [PATCH 07/10] fix --- .expect/last-tested | 1 + .expect/plan-go-to-nisa-32aebb.json | 35 + .../src/components/screens/results-screen.tsx | 13 + .../src/components/screens/testing-screen.tsx | 12 +- apps/cli/src/constants.ts | 2 + apps/cli/src/index.tsx | 4 + apps/cli/src/live-view-url.ts | 6 + apps/cli/vite.config.ts | 10 +- apps/website/next-env.d.ts | 2 +- packages/browser/package.json | 2 +- packages/browser/src/mcp/mcp-session.ts | 176 +- packages/browser/src/runtime/index.ts | 2 +- packages/recorder/package.json | 2 +- packages/recorder/src/index.ts | 4 +- packages/recorder/src/live-view-server.ts | 95 +- packages/recorder/src/replay-broadcast.ts | 40 + packages/recorder/src/replay-log.ts | 27 - .../recorder/src/utils/evaluate-runtime.ts | 2 +- packages/recorder/viewer/src/app.tsx | 117 +- pnpm-lock.yaml | 4640 ++++++++++------- 20 files changed, 3117 insertions(+), 2075 deletions(-) create mode 100644 .expect/last-tested create mode 100644 .expect/plan-go-to-nisa-32aebb.json create mode 100644 apps/cli/src/live-view-url.ts create mode 100644 packages/recorder/src/replay-broadcast.ts delete mode 100644 packages/recorder/src/replay-log.ts diff --git a/.expect/last-tested b/.expect/last-tested new file mode 100644 index 00000000..23f5413c --- /dev/null +++ b/.expect/last-tested @@ -0,0 +1 @@ +1103d4cd3dac4f6a1a6716a9af8af3a8341e23bb44e41581c53bfa9c22205fc6 \ No newline at end of file diff --git a/.expect/plan-go-to-nisa-32aebb.json b/.expect/plan-go-to-nisa-32aebb.json new file mode 100644 index 00000000..9cf3da8f --- /dev/null +++ b/.expect/plan-go-to-nisa-32aebb.json @@ -0,0 +1,35 @@ +{ + "id": "plan-01", + "title": "Visit nisarg.io and observe page content", + "rationale": "The user wants to navigate to nisarg.io and get a description of what is visible on the page. Steps cover navigation, waiting for content to load, and capturing key visual elements.", + "steps": [ + { + "id": "step-01", + "title": "Navigate to nisarg.io", + "instruction": "Navigate the browser to https://nisarg.io and wait for the page to fully load.", + "expectedOutcome": "The page at nisarg.io loads successfully and the document title or main heading is visible.", + "routeHint": "/" + }, + { + "id": "step-02", + "title": "Observe the page header and hero section", + "instruction": "Look at the top of the page and identify the site name, navigation links, and any hero/banner section including headings, taglines, or introductory text.", + "expectedOutcome": "The page header, site branding, and primary heading or hero content are visible and can be described.", + "routeHint": "/" + }, + { + "id": "step-03", + "title": "Observe the main content area", + "instruction": "Scroll down if needed and examine the main content area of the page. Identify any blog posts, project listings, about text, or other primary content sections.", + "expectedOutcome": "The main content sections are visible, including any article titles, project names, or descriptive text.", + "routeHint": "/" + }, + { + "id": "step-04", + "title": "Observe the footer and overall layout", + "instruction": "Scroll to the bottom of the page and note the footer content, including any social links, copyright info, or additional navigation. Take a screenshot to capture the overall visual style and color scheme.", + "expectedOutcome": "The footer is visible and the overall page layout, color scheme, and visual style can be described.", + "routeHint": "/" + } + ] +} diff --git a/apps/cli/src/components/screens/results-screen.tsx b/apps/cli/src/components/screens/results-screen.tsx index d20d9fce..8bcdbd1a 100644 --- a/apps/cli/src/components/screens/results-screen.tsx +++ b/apps/cli/src/components/screens/results-screen.tsx @@ -3,6 +3,8 @@ import { Box, Text, useInput } from "ink"; import { Option } from "effect"; import type { TestReport } from "@expect/supervisor"; import { copyToClipboard } from "../../utils/copy-to-clipboard.js"; +import { openUrl } from "../../utils/open-url.js"; +import { LIVE_VIEW_URL } from "../../live-view-url.js"; import { useColors } from "../theme-context.js"; import { RuledBox } from "../ui/ruled-box.js"; import { ScreenHeading } from "../ui/screen-heading.js"; @@ -52,6 +54,9 @@ export const ResultsScreen = ({ report }: ResultsScreenProps) => { if (normalizedInput === "p") { handlePostPullRequestComment(); } + if (normalizedInput === "o") { + openUrl(LIVE_VIEW_URL); + } }); return ( @@ -111,6 +116,14 @@ export const ResultsScreen = ({ report }: ResultsScreenProps) => { {clipboardError ? {clipboardError} : null} + + openUrl(LIVE_VIEW_URL)}> + + Press o to open live view in browser. + + + + {Option.isSome(report.pullRequest) ? ( diff --git a/apps/cli/src/components/screens/testing-screen.tsx b/apps/cli/src/components/screens/testing-screen.tsx index 0f097bce..2ea99975 100644 --- a/apps/cli/src/components/screens/testing-screen.tsx +++ b/apps/cli/src/components/screens/testing-screen.tsx @@ -24,6 +24,8 @@ import { usePlanStore } from "../../stores/use-plan-store.js"; import { usePlanExecutionStore } from "../../stores/use-plan-execution-store.js"; import { usePreferencesStore } from "../../stores/use-preferences.js"; import { useNavigationStore, Screen } from "../../stores/use-navigation.js"; +import { LIVE_VIEW_URL } from "../../live-view-url.js"; +import { openUrl } from "../../utils/open-url.js"; import { ScreenHeading } from "../ui/screen-heading.js"; import cliTruncate from "cli-truncate"; import { formatElapsedTime } from "../../utils/format-elapsed-time.js"; @@ -138,6 +140,11 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = return; } + if (normalizedInput === "o" && (isPlanning || isExecutingPlan)) { + openUrl(LIVE_VIEW_URL); + return; + } + if (key.escape) { if (AsyncResult.isFailure(planResult) || AsyncResult.isFailure(executionResult)) { goToMain(); @@ -276,12 +283,15 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = ) : null} {(isExecutingPlan || isPlanning) && !showCancelConfirmation ? ( - + + + Press o to open live view in browser. + ) : null} diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index 08a79b3c..34c4e01d 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -29,6 +29,8 @@ export const ALT_SCREEN_ON = "\u001b[?1049h\u001b[2J\u001b[H"; export const ALT_SCREEN_OFF = "\u001b[?1049l"; export const FALLBACK_TERMINAL_COLUMNS = 80; export const FALLBACK_TERMINAL_ROWS = 24; +export const LIVE_VIEW_PORT_RANGE_START = 17400; +export const LIVE_VIEW_PORT_RANGE_SIZE = 600; export const LIVE_VIEW_READY_POLL_INTERVAL_MS = 1000; export const CLICK_SUPPORT_ENABLED = process.env.SUPPORT_CLICK === "true" || process.env.SUPPORT_CLICK === "1"; diff --git a/apps/cli/src/index.tsx b/apps/cli/src/index.tsx index cc69510f..29da5fd8 100644 --- a/apps/cli/src/index.tsx +++ b/apps/cli/src/index.tsx @@ -24,6 +24,10 @@ import { queryClient } from "./query-client.js"; import { setInkInstance } from "./utils/clear-ink-display.js"; import { RegistryProvider } from "@effect/atom-react"; import { agentProviderAtom } from "./data/runtime.js"; +import { LIVE_VIEW_URL } from "./live-view-url.js"; +import { EXPECT_LIVE_VIEW_URL_ENV_NAME } from "@expect/browser/mcp"; + +process.env[EXPECT_LIVE_VIEW_URL_ENV_NAME] = LIVE_VIEW_URL; process.env.BROWSER_TESTER_REPLAY_OUTPUT_PATH = join( process.cwd(), diff --git a/apps/cli/src/live-view-url.ts b/apps/cli/src/live-view-url.ts new file mode 100644 index 00000000..2fe816da --- /dev/null +++ b/apps/cli/src/live-view-url.ts @@ -0,0 +1,6 @@ +import { LIVE_VIEW_PORT_RANGE_START, LIVE_VIEW_PORT_RANGE_SIZE } from "./constants.js"; + +const liveViewPort = + LIVE_VIEW_PORT_RANGE_START + Math.floor(Math.random() * LIVE_VIEW_PORT_RANGE_SIZE); + +export const LIVE_VIEW_URL = `http://127.0.0.1:${liveViewPort}`; diff --git a/apps/cli/vite.config.ts b/apps/cli/vite.config.ts index 346ed514..24fbc6af 100644 --- a/apps/cli/vite.config.ts +++ b/apps/cli/vite.config.ts @@ -13,7 +13,15 @@ export default defineConfig({ banner: "#!/usr/bin/env node", deps: { alwaysBundle: [/^@expect\//], - neverBundle: ["playwright", "playwright-core", "chromium-bidi", "libsql", "ws", "undici"], + neverBundle: [ + "playwright", + "playwright-core", + "chromium-bidi", + "libsql", + "ws", + "undici", + "vite", + ], }, plugins: [reactCompilerPlugin()], }, diff --git a/apps/website/next-env.d.ts b/apps/website/next-env.d.ts index c4b7818f..9edff1c7 100644 --- a/apps/website/next-env.d.ts +++ b/apps/website/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/packages/browser/package.json b/packages/browser/package.json index 323e7d98..538dfe4f 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -38,9 +38,9 @@ "typecheck": "tsgo --noEmit" }, "dependencies": { - "@browser-tester/recorder": "workspace:*", "@effect/platform-node": "4.0.0-beta.28", "@expect/cookies": "workspace:*", + "@expect/recorder": "workspace:*", "@medv/finder": "^4.0.2", "@modelcontextprotocol/sdk": "^1.27.1", "effect": "4.0.0-beta.28", diff --git a/packages/browser/src/mcp/mcp-session.ts b/packages/browser/src/mcp/mcp-session.ts index ad5b2f38..8af14d4c 100644 --- a/packages/browser/src/mcp/mcp-session.ts +++ b/packages/browser/src/mcp/mcp-session.ts @@ -1,19 +1,19 @@ import { basename, dirname, extname, join } from "node:path"; +import { mkdirSync, writeFileSync } from "node:fs"; import type { Browser as PlaywrightBrowser, BrowserContext, Page } from "playwright"; import { Config, Effect, Fiber, Layer, Option, Ref, Schedule, ServiceMap } from "effect"; -import { FileSystem } from "effect/FileSystem"; import { collectAllEvents, - createReplayLog, evaluateRecorderRuntime, buildReplayViewerHtml, + makeReplayBroadcast, startLiveViewServer, EVENT_COLLECT_INTERVAL_MS, type eventWithTime, type LiveViewHandle, - type ReplayLog, + type ReplayBroadcast, type ViewerRunState, -} from "@browser-tester/recorder"; +} from "@expect/recorder"; import { Browser } from "../browser"; import { NavigationError } from "../errors"; import type { AnnotatedScreenshotOptions, SnapshotOptions, SnapshotResult } from "../types"; @@ -41,8 +41,7 @@ export interface BrowserSessionData { readonly consoleMessages: ConsoleEntry[]; readonly networkRequests: NetworkEntry[]; readonly replayOutputPath: string | undefined; - readonly replayLog: ReplayLog | undefined; - readonly accumulatedReplayEvents: eventWithTime[]; + readonly broadcast: ReplayBroadcast; readonly trackedPages: Set; lastSnapshot: SnapshotResult | undefined; } @@ -92,17 +91,54 @@ const setupPageTracking = (page: Page, sessionData: BrowserSessionData) => { }); }; +const flushBroadcastToFile = (broadcast: ReplayBroadcast, outputPath: string) => + Effect.gen(function* () { + const allEvents = yield* broadcast.snapshotEvents; + if (allEvents.length === 0) return; + + const ndjson = allEvents.map((event) => JSON.stringify(event)).join("\n") + "\n"; + mkdirSync(dirname(outputPath), { recursive: true }); + writeFileSync(outputPath, ndjson); + + const runState = yield* broadcast.snapshotRunState; + const replayFileName = basename(outputPath); + const replayBaseName = basename(outputPath, extname(outputPath)); + const htmlReportPath = join(dirname(outputPath), `${replayBaseName}.html`); + const reportHtml = buildReplayViewerHtml({ + title: runState ? `Test Report: ${runState.title}` : "Expect Report", + eventsSource: { ndjsonPath: replayFileName }, + steps: runState, + }); + writeFileSync(htmlReportPath, reportHtml); + + const runStateFilePath = join(dirname(outputPath), "run-state.json"); + if (runState) { + writeFileSync(runStateFilePath, JSON.stringify(runState)); + } + }); + export class McpSession extends ServiceMap.Service()("@browser/McpSession", { make: Effect.gen(function* () { const browserService = yield* Browser; - const fileSystem = yield* FileSystem; const replayOutputPath = yield* Config.option(Config.string(EXPECT_REPLAY_OUTPUT_ENV_NAME)); const liveViewUrl = yield* Config.option(Config.string(EXPECT_LIVE_VIEW_URL_ENV_NAME)); const sessionRef = yield* Ref.make(undefined); const liveViewRef = yield* Ref.make(undefined); const pollingFiberRef = yield* Ref.make | undefined>(undefined); - const latestRunStateRef = yield* Ref.make(undefined); + const broadcastRef = yield* Ref.make(undefined); + const outputPathRef = yield* Ref.make(undefined); + + yield* Effect.addFinalizer(() => + Effect.gen(function* () { + const broadcast = yield* Ref.get(broadcastRef); + const outputPath = yield* Ref.get(outputPathRef); + if (!broadcast || !outputPath) return; + yield* flushBroadcastToFile(broadcast, outputPath).pipe( + Effect.catchCause((cause) => Effect.logDebug("Finalizer flush failed", { cause })), + ); + }), + ); const requireSession = Effect.fn("McpSession.requireSession")(function* () { const session = yield* Ref.get(sessionRef); @@ -130,9 +166,10 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe }); const pushStepEvent = Effect.fn("McpSession.pushStepEvent")(function* (state: ViewerRunState) { - yield* Ref.set(latestRunStateRef, state); - const session = yield* Ref.get(sessionRef); - session?.replayLog?.writeRunState(state); + const broadcast = yield* Ref.get(broadcastRef); + if (broadcast) { + yield* broadcast.publishRunState(state); + } }); const open = Effect.fn("McpSession.open")(function* (url: string, options: OpenOptions = {}) { @@ -144,7 +181,11 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe waitUntil: options.waitUntil, }); + const broadcast = yield* makeReplayBroadcast; + yield* Ref.set(broadcastRef, broadcast); + const outputPath = Option.getOrUndefined(replayOutputPath); + yield* Ref.set(outputPathRef, outputPath); const sessionData: BrowserSessionData = { browser: pageResult.browser, context: pageResult.context, @@ -152,8 +193,7 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe consoleMessages: [], networkRequests: [], replayOutputPath: outputPath, - replayLog: outputPath ? createReplayLog(outputPath) : undefined, - accumulatedReplayEvents: [], + broadcast, trackedPages: new Set(), lastSnapshot: undefined, }; @@ -168,15 +208,12 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe Effect.flatMap((page) => { if (!page || page.isClosed()) return Effect.void; return evaluateRecorderRuntime(page, "getEvents").pipe( - Effect.tap((events) => - Effect.sync(() => { - if (Array.isArray(events) && events.length > 0) { - const session = Ref.getUnsafe(sessionRef); - session?.accumulatedReplayEvents.push(...events); - session?.replayLog?.appendEvents(events); - } - }), - ), + Effect.tap((events) => { + if (Array.isArray(events) && events.length > 0) { + return broadcast.publishEvents(events); + } + return Effect.void; + }), Effect.catchCause((cause) => Effect.logDebug("Replay event collection failed", { cause }), ), @@ -192,7 +229,7 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe const existingLiveView = yield* Ref.get(liveViewRef); if (Option.isSome(liveViewUrl) && !existingLiveView) { - const handle = yield* startLiveViewServer(liveViewUrl.value).pipe( + const handle = yield* startLiveViewServer(liveViewUrl.value, broadcast).pipe( Effect.catchCause((cause) => Effect.logDebug("Live view server failed to start", { cause }).pipe( Effect.as(undefined), @@ -247,6 +284,19 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe yield* Ref.set(pollingFiberRef, undefined); } + if (!activeSession.page.isClosed()) { + const finalEvents = yield* collectAllEvents(activeSession.page).pipe( + Effect.catchCause((cause) => + Effect.logDebug("Failed to collect final replay events", { cause }).pipe( + Effect.as([] as ReadonlyArray), + ), + ), + ); + if (finalEvents.length > 0) { + yield* activeSession.broadcast.publishEvents(finalEvents); + } + } + const liveView = yield* Ref.get(liveViewRef); if (liveView) { yield* liveView.close.pipe( @@ -255,79 +305,21 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe yield* Ref.set(liveViewRef, undefined); } - let replaySessionPath: string | undefined; - let reportPath: string | undefined; - - yield* Effect.gen(function* () { - if (!activeSession.page.isClosed()) { - const finalEvents = yield* collectAllEvents(activeSession.page).pipe( - Effect.catchCause((cause) => - Effect.logDebug("Failed to collect final replay events", { cause }).pipe( - Effect.as([] as ReadonlyArray), - ), - ), - ); - if (finalEvents.length > 0) { - activeSession.accumulatedReplayEvents.push(...finalEvents); - activeSession.replayLog?.appendEvents(finalEvents); - } - } - - const resolvedReplayOutputPath = activeSession.replayOutputPath; - if (resolvedReplayOutputPath && activeSession.accumulatedReplayEvents.length > 0) { - if (!activeSession.replayLog) { - const ndjson = - activeSession.accumulatedReplayEvents - .map((event) => JSON.stringify(event)) - .join("\n") + "\n"; - - yield* fileSystem - .makeDirectory(dirname(resolvedReplayOutputPath), { recursive: true }) - .pipe( - Effect.catchCause((cause) => - Effect.logDebug("Failed to create replay output directory", { cause }), - ), - ); - yield* fileSystem - .writeFileString(resolvedReplayOutputPath, ndjson) - .pipe( - Effect.catchCause((cause) => - Effect.logDebug("Failed to write replay file", { cause }), - ), - ); - } - replaySessionPath = resolvedReplayOutputPath; - - const runState = yield* Ref.get(latestRunStateRef); - const replayFileName = basename(resolvedReplayOutputPath); - const replayBaseName = basename( - resolvedReplayOutputPath, - extname(resolvedReplayOutputPath), - ); - const htmlReportPath = join(dirname(resolvedReplayOutputPath), `${replayBaseName}.html`); - const reportHtml = buildReplayViewerHtml({ - title: runState ? `Test Report: ${runState.title}` : "Expect Report", - eventsSource: { ndjsonPath: replayFileName }, - steps: runState, - }); - - yield* fileSystem - .writeFileString(htmlReportPath, reportHtml) - .pipe( - Effect.catchCause((cause) => - Effect.logDebug("Failed to write HTML report", { cause }), - ), - ); - reportPath = htmlReportPath; - } - }).pipe( - Effect.catchCause((cause) => Effect.logDebug("Failed during close cleanup", { cause })), - ); - yield* Effect.tryPromise(() => activeSession.browser.close()).pipe( Effect.catchCause((cause) => Effect.logDebug("Failed to close browser", { cause })), ); + const resolvedOutputPath = activeSession.replayOutputPath; + let replaySessionPath: string | undefined; + let reportPath: string | undefined; + if (resolvedOutputPath) { + replaySessionPath = resolvedOutputPath; + reportPath = join( + dirname(resolvedOutputPath), + `${basename(resolvedOutputPath, extname(resolvedOutputPath))}.html`, + ); + } + return { replaySessionPath, reportPath } satisfies CloseResult; }); diff --git a/packages/browser/src/runtime/index.ts b/packages/browser/src/runtime/index.ts index 2dbf4c35..1a41240d 100644 --- a/packages/browser/src/runtime/index.ts +++ b/packages/browser/src/runtime/index.ts @@ -5,7 +5,7 @@ export { getEvents, getAllEvents, getEventCount, -} from "@browser-tester/recorder/runtime"; +} from "@expect/recorder/runtime"; interface OverlayItem { label: number; diff --git a/packages/recorder/package.json b/packages/recorder/package.json index aaa3eaef..7d831bfb 100644 --- a/packages/recorder/package.json +++ b/packages/recorder/package.json @@ -1,5 +1,5 @@ { - "name": "@browser-tester/recorder", + "name": "@expect/recorder", "version": "0.0.1", "private": true, "files": [ diff --git a/packages/recorder/src/index.ts b/packages/recorder/src/index.ts index 236bb90d..64d7d10a 100644 --- a/packages/recorder/src/index.ts +++ b/packages/recorder/src/index.ts @@ -2,8 +2,8 @@ export { collectEvents, collectAllEvents, loadSession } from "./recorder"; export { buildReplayViewerHtml } from "./replay-viewer"; export { startLiveViewServer } from "./live-view-server"; export type { LiveViewHandle } from "./live-view-server"; -export { createReplayLog } from "./replay-log"; -export type { ReplayLog } from "./replay-log"; +export { makeReplayBroadcast } from "./replay-broadcast"; +export type { ReplayBroadcast } from "./replay-broadcast"; export { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; export { RecorderInjectionError, SessionLoadError } from "./errors"; export type { CollectResult } from "./types"; diff --git a/packages/recorder/src/live-view-server.ts b/packages/recorder/src/live-view-server.ts index aef397e4..d0620cfe 100644 --- a/packages/recorder/src/live-view-server.ts +++ b/packages/recorder/src/live-view-server.ts @@ -1,6 +1,8 @@ +import type { ServerResponse } from "node:http"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { Effect } from "effect"; +import { Effect, Fiber, PubSub, Queue, Ref } from "effect"; +import type { ReplayBroadcast } from "./replay-broadcast"; const VIEWER_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../viewer"); @@ -9,10 +11,46 @@ export interface LiveViewHandle { readonly close: Effect.Effect; } +const drainToSse = (broadcast: ReplayBroadcast, response: ServerResponse) => + Effect.scoped( + Effect.gen(function* () { + const snapshot = yield* broadcast.snapshotEvents; + if (snapshot.length > 0) { + response.write(`event: replay\ndata: ${JSON.stringify(snapshot)}\n\n`); + } + + const runState = yield* broadcast.snapshotRunState; + if (runState) { + response.write(`event: steps\ndata: ${JSON.stringify(runState)}\n\n`); + } + + const eventsQueue = yield* PubSub.subscribe(broadcast.eventsPubSub); + const stepsQueue = yield* PubSub.subscribe(broadcast.runStatePubSub); + + const forwardEvents = Effect.forever( + Effect.gen(function* () { + const batch = yield* Queue.take(eventsQueue); + response.write(`event: replay\ndata: ${JSON.stringify(batch)}\n\n`); + }), + ); + + const forwardSteps = Effect.forever( + Effect.gen(function* () { + const state = yield* Queue.take(stepsQueue); + response.write(`event: steps\ndata: ${JSON.stringify(state)}\n\n`); + }), + ); + + yield* Effect.all([forwardEvents, forwardSteps], { concurrency: "unbounded" }); + }), + ); + export const startLiveViewServer = Effect.fn("LiveViewServer.start")(function* ( liveViewUrl: string, + broadcast: ReplayBroadcast, ) { const parsedUrl = new URL(liveViewUrl); + const activeFibers = yield* Ref.make>>(new Map()); const { createServer: createViteServer } = yield* Effect.tryPromise({ try: () => import("vite"), @@ -29,6 +67,49 @@ export const startLiveViewServer = Effect.fn("LiveViewServer.start")(function* ( strictPort: true, }, logLevel: "silent", + plugins: [ + { + name: "expect-sse", + configureServer(server) { + server.middlewares.use((request, response, next) => { + if (request.url === "/events") { + response.writeHead(200, { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }); + + const fiber = Effect.runFork(drainToSse(broadcast, response)); + Ref.getUnsafe(activeFibers).set(response, fiber); + + request.on("close", () => { + Ref.getUnsafe(activeFibers).delete(response); + Effect.runFork(Fiber.interrupt(fiber)); + }); + return; + } + + if (request.url === "/latest.json") { + Effect.runPromise(broadcast.snapshotEvents).then((events) => { + response.writeHead(200, { "Content-Type": "application/json" }); + response.end(JSON.stringify(events)); + }); + return; + } + + if (request.url === "/run-state.json") { + Effect.runPromise(broadcast.snapshotRunState).then((runState) => { + response.writeHead(200, { "Content-Type": "application/json" }); + response.end(JSON.stringify(runState ?? undefined)); + }); + return; + } + + next(); + }); + }, + }, + ], }), catch: (cause) => new Error(`Failed to create Vite server: ${cause}`), }); @@ -40,9 +121,15 @@ export const startLiveViewServer = Effect.fn("LiveViewServer.start")(function* ( return { url: parsedUrl.toString(), - close: Effect.tryPromise({ - try: () => viteServer.close(), - catch: () => new Error("Failed to close Vite server"), + close: Effect.gen(function* () { + const fibers = yield* Ref.get(activeFibers); + yield* Effect.forEach([...fibers.values()], (fiber) => Fiber.interrupt(fiber), { + concurrency: "unbounded", + }); + yield* Effect.tryPromise({ + try: () => viteServer.close(), + catch: () => new Error("Failed to close Vite server"), + }); }), } satisfies LiveViewHandle; }); diff --git a/packages/recorder/src/replay-broadcast.ts b/packages/recorder/src/replay-broadcast.ts new file mode 100644 index 00000000..b628e7fe --- /dev/null +++ b/packages/recorder/src/replay-broadcast.ts @@ -0,0 +1,40 @@ +import { Effect, PubSub, Ref } from "effect"; +import type { eventWithTime } from "@rrweb/types"; +import type { ViewerRunState } from "./viewer-events"; + +export interface ReplayBroadcast { + readonly publishEvents: (events: readonly eventWithTime[]) => Effect.Effect; + readonly publishRunState: (state: ViewerRunState) => Effect.Effect; + readonly snapshotEvents: Effect.Effect; + readonly snapshotRunState: Effect.Effect; + readonly eventsPubSub: PubSub.PubSub; + readonly runStatePubSub: PubSub.PubSub; +} + +export const makeReplayBroadcast = Effect.gen(function* () { + const eventsRef = yield* Ref.make([]); + const runStateRef = yield* Ref.make(undefined); + const eventsPubSub = yield* PubSub.unbounded(); + const runStatePubSub = yield* PubSub.unbounded(); + + const publishEvents = (events: readonly eventWithTime[]) => + Effect.gen(function* () { + yield* Ref.update(eventsRef, (previous) => [...previous, ...events]); + yield* PubSub.publish(eventsPubSub, events); + }); + + const publishRunState = (state: ViewerRunState) => + Effect.gen(function* () { + yield* Ref.set(runStateRef, state); + yield* PubSub.publish(runStatePubSub, state); + }); + + return { + publishEvents, + publishRunState, + snapshotEvents: Ref.get(eventsRef), + snapshotRunState: Ref.get(runStateRef), + eventsPubSub, + runStatePubSub, + } satisfies ReplayBroadcast; +}); diff --git a/packages/recorder/src/replay-log.ts b/packages/recorder/src/replay-log.ts deleted file mode 100644 index ca4f1c3e..00000000 --- a/packages/recorder/src/replay-log.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { appendFileSync, mkdirSync, writeFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import type { eventWithTime } from "@rrweb/types"; -import { RUN_STATE_FILE_NAME } from "./constants"; -import type { ViewerRunState } from "./viewer-events"; - -export interface ReplayLog { - readonly appendEvents: (events: readonly eventWithTime[]) => void; - readonly writeRunState: (state: ViewerRunState) => void; -} - -export const createReplayLog = (ndjsonPath: string): ReplayLog => { - const directory = dirname(ndjsonPath); - mkdirSync(directory, { recursive: true }); - writeFileSync(ndjsonPath, ""); - - return { - appendEvents: (events) => { - if (events.length === 0) return; - const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n"; - appendFileSync(ndjsonPath, lines); - }, - writeRunState: (state) => { - writeFileSync(join(directory, RUN_STATE_FILE_NAME), JSON.stringify(state)); - }, - }; -}; diff --git a/packages/recorder/src/utils/evaluate-runtime.ts b/packages/recorder/src/utils/evaluate-runtime.ts index a6347a4f..61670747 100644 --- a/packages/recorder/src/utils/evaluate-runtime.ts +++ b/packages/recorder/src/utils/evaluate-runtime.ts @@ -13,7 +13,7 @@ export const evaluateRecorderRuntime = ( try: () => page.evaluate( ({ method, args }: { method: string; args: unknown[] }) => { - const runtime = Reflect.get(globalThis, "__browserTesterRuntime"); + const runtime = Reflect.get(globalThis, "__EXPECT_RUNTIME__"); if (!runtime || typeof runtime !== "object") { throw new Error("Browser runtime is not initialized"); } diff --git a/packages/recorder/viewer/src/app.tsx b/packages/recorder/viewer/src/app.tsx index 2e9c26aa..862f80fc 100644 --- a/packages/recorder/viewer/src/app.tsx +++ b/packages/recorder/viewer/src/app.tsx @@ -2,92 +2,97 @@ import { useCallback, useRef, useState } from "react"; import rrwebPlayer from "rrweb-player"; import "rrweb-player/dist/style.css"; import type { eventWithTime } from "@rrweb/types"; -import { - EVENT_COLLECT_INTERVAL_MS, - REPLAY_FILE_NAME, - REPLAY_PLAYER_HEIGHT_PX, - REPLAY_PLAYER_WIDTH_PX, - RUN_STATE_FILE_NAME, - EXPECT_STATE_DIR, -} from "../../src/constants"; +import { REPLAY_PLAYER_HEIGHT_PX, REPLAY_PLAYER_WIDTH_PX } from "../../src/constants"; import type { ViewerRunState } from "../../src/viewer-events"; import { useMountEffect } from "./hooks/use-mount-effect"; import { StepsPanel } from "./steps-panel"; -const REPLAY_URL = `/${EXPECT_STATE_DIR}/${REPLAY_FILE_NAME}`; -const RUN_STATE_URL = `/${EXPECT_STATE_DIR}/${RUN_STATE_FILE_NAME}`; - export const App = () => { const [runState, setRunState] = useState(); const [status, setStatus] = useState("Waiting for test run\u2026"); const containerRef = useRef(null); const playerRef = useRef(); const eventsRef = useRef([]); - const eventCountRef = useRef(0); - const initPlayer = useCallback((events: eventWithTime[]) => { - if (playerRef.current) { - playerRef.current.getReplayer().addEvent(events.at(-1)!); - return; + const addEvents = useCallback((events: readonly eventWithTime[]) => { + for (const event of events) { + eventsRef.current.push(event); + if (playerRef.current) { + playerRef.current.getReplayer().addEvent(event); + } } - if (events.length < 2 || !containerRef.current) return; - - setStatus(""); - playerRef.current = new rrwebPlayer({ - target: containerRef.current, - props: { - events, - width: REPLAY_PLAYER_WIDTH_PX, - height: REPLAY_PLAYER_HEIGHT_PX, - autoPlay: true, - showController: true, - }, - }); + if (!playerRef.current && eventsRef.current.length >= 2 && containerRef.current) { + setStatus(""); + playerRef.current = new rrwebPlayer({ + target: containerRef.current, + props: { + events: eventsRef.current, + width: REPLAY_PLAYER_WIDTH_PX, + height: REPLAY_PLAYER_HEIGHT_PX, + autoPlay: true, + showController: true, + liveMode: true, + }, + }); + playerRef.current.getReplayer().startLive(); + } + if (events.length > 0) setStatus(""); }, []); useMountEffect(() => { - const poll = async () => { + const eventSource = new EventSource("/events"); + let sseConnected = false; + + eventSource.addEventListener("replay", (message) => { + sseConnected = true; + try { + const events: eventWithTime[] = JSON.parse(message.data); + addEvents(events); + } catch { + /* malformed event */ + } + }); + + eventSource.addEventListener("steps", (message) => { + try { + const state: ViewerRunState = JSON.parse(message.data); + if (state?.steps) setRunState(state); + } catch { + /* malformed state */ + } + }); + + eventSource.onerror = () => { + if (sseConnected) { + setStatus("Session ended. Loading snapshot\u2026"); + eventSource.close(); + fallbackToSnapshot(); + } + }; + + const fallbackToSnapshot = async () => { try { - const replayResponse = await fetch(REPLAY_URL); + const replayResponse = await fetch("/latest.json"); if (replayResponse.ok) { - const text = await replayResponse.text(); - if (text.trim()) { - const lines = text.trim().split("\n"); - if (lines.length > eventCountRef.current) { - const newLines = lines.slice(eventCountRef.current); - for (const line of newLines) { - const event: eventWithTime = JSON.parse(line); - eventsRef.current.push(event); - if (playerRef.current) { - playerRef.current.getReplayer().addEvent(event); - } - } - eventCountRef.current = lines.length; - if (!playerRef.current && eventsRef.current.length >= 2) { - initPlayer(eventsRef.current); - } - setStatus(""); - } - } + const events: eventWithTime[] = await replayResponse.json(); + if (events.length > 0) addEvents(events); } } catch { - /* file not available yet */ + /* snapshot not available */ } try { - const stateResponse = await fetch(RUN_STATE_URL); + const stateResponse = await fetch("/run-state.json"); if (stateResponse.ok) { const state: ViewerRunState = await stateResponse.json(); if (state?.steps) setRunState(state); } } catch { - /* run state not available yet */ + /* state not available */ } }; - poll(); - const interval = setInterval(poll, EVENT_COLLECT_INTERVAL_MS); - return () => clearInterval(interval); + return () => eventSource.close(); }); return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 697ed29e..9312fe25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,15 +33,15 @@ importers: apps/cli: dependencies: - '@browser-tester/shared': + '@effect/atom-react': + specifier: 4.0.0-beta.35 + version: 4.0.0-beta.35(effect@4.0.0-beta.28)(react@19.2.4)(scheduler@0.27.0) + '@expect/shared': specifier: workspace:* version: link:../../packages/shared - '@browser-tester/supervisor': + '@expect/supervisor': specifier: workspace:* version: link:../../packages/supervisor - '@effect/atom-react': - specifier: 4.0.0-beta.35 - version: 4.0.0-beta.35(effect@4.0.0-beta.28)(react@19.2.4)(scheduler@0.27.0) '@tanstack/react-query': specifier: ^5.80.7 version: 5.90.21(react@19.2.4) @@ -107,8 +107,8 @@ importers: apps/website: dependencies: '@base-ui/react': - specifier: ^1.2.0 - version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: ^1.3.0 + version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -116,32 +116,41 @@ importers: specifier: ^2.1.1 version: 2.1.1 lucide-react: - specifier: ^0.576.0 - version: 0.576.0(react@19.2.3) + specifier: ^0.577.0 + version: 0.577.0(react@19.2.4) motion: - specifier: ^12.35.0 - version: 12.36.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: ^12.38.0 + version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: - specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 16.2.0 + version: 16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next-themes: specifier: ^0.4.6 - version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - radix-ui: - specifier: ^1.4.3 - version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: - specifier: 19.2.3 - version: 19.2.3 + specifier: 19.2.4 + version: 19.2.4 react-dom: - specifier: 19.2.3 - version: 19.2.3(react@19.2.3) + specifier: 19.2.4 + version: 19.2.4(react@19.2.4) + react-grab: + specifier: ^0.1.28 + version: 0.1.28(@types/react@19.2.14)(react@19.2.4) + shadcn: + specifier: ^4.0.8 + version: 4.1.0(@types/node@20.19.37)(typescript@5.9.3) tailwind-merge: specifier: ^3.5.0 version: 3.5.0 - zustand: - specifier: ^5.0.11 - version: 5.0.11(@types/react@19.2.14)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) + torph: + specifier: ^0.0.9 + version: 0.0.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + web-haptics: + specifier: ^0.0.6 + version: 0.0.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) devDependencies: '@tailwindcss/postcss': specifier: ^4 @@ -155,18 +164,15 @@ importers: '@types/react-dom': specifier: ^19 version: 19.2.3(@types/react@19.2.14) - react-grab: - specifier: ^0.1.28 - version: 0.1.28(@types/react@19.2.14)(react@19.2.3) - shadcn: - specifier: ^3.8.5 - version: 3.8.5(@types/node@20.19.37)(typescript@5.9.3) + eslint: + specifier: ^9 + version: 9.39.4(jiti@2.6.1) + eslint-config-next: + specifier: 16.2.0 + version: 16.2.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) tailwindcss: specifier: ^4 version: 4.2.1 - tw-animate-css: - specifier: ^1.4.0 - version: 1.4.0 typescript: specifier: ^5 version: 5.9.3 @@ -179,10 +185,10 @@ importers: '@ai-sdk/provider': specifier: ^3.0.8 version: 3.0.8 - '@browser-tester/browser': + '@expect/browser': specifier: workspace:* version: link:../browser - '@browser-tester/shared': + '@expect/shared': specifier: workspace:* version: link:../shared '@zed-industries/claude-agent-acp': @@ -207,15 +213,15 @@ importers: packages/browser: dependencies: - '@browser-tester/cookies': + '@effect/platform-node': + specifier: 4.0.0-beta.28 + version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) + '@expect/cookies': specifier: workspace:* version: link:../cookies - '@browser-tester/recorder': + '@expect/recorder': specifier: workspace:* version: link:../recorder - '@effect/platform-node': - specifier: 4.0.0-beta.28 - version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) '@medv/finder': specifier: ^4.0.2 version: 4.0.2 @@ -273,6 +279,10 @@ importers: specifier: ^5.7.0 version: 5.9.3 + packages/expect: {} + + packages/expect-skill: {} + packages/recorder: dependencies: '@effect/platform-node': @@ -361,18 +371,18 @@ importers: '@ai-sdk/provider': specifier: ^3.0.8 version: 3.0.8 - '@browser-tester/agent': + '@effect/platform-node': + specifier: 4.0.0-beta.28 + version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) + '@expect/agent': specifier: workspace:* version: link:../agent - '@browser-tester/browser': + '@expect/browser': specifier: workspace:* version: link:../browser - '@browser-tester/shared': + '@expect/shared': specifier: workspace:* version: link:../shared - '@effect/platform-node': - specifier: 4.0.0-beta.28 - version: 4.0.0-beta.28(effect@4.0.0-beta.28)(ioredis@5.10.0) effect: specifier: 4.0.0-beta.28 version: 4.0.0-beta.28 @@ -393,15 +403,11 @@ importers: specifier: ^5.7.0 version: 5.9.3 - packages/testie: {} - - packages/testie-skill: {} - playground/minecraft-ui: dependencies: next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + version: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: specifier: 19.2.3 version: 19.2.3 @@ -700,9 +706,15 @@ packages: effect: ^4.0.0-beta.28 ioredis: ^5.7.0 + '@emnapi/core@1.9.1': + resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} + '@emnapi/runtime@1.9.0': resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} + '@emnapi/wasi-threads@1.2.0': + resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -1015,6 +1027,44 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + 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/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + 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/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -1036,6 +1086,22 @@ packages: peerDependencies: hono: ^4 + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} @@ -1356,24 +1422,45 @@ packages: resolution: {integrity: sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==} engines: {node: '>=18'} + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} '@next/env@16.1.6': resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} + '@next/env@16.2.0': + resolution: {integrity: sha512-OZIbODWWAi0epQRCRjNe1VO45LOFBzgiyqmTLzIqWq6u1wrxKnAyz1HH6tgY/Mc81YzIjRPoYsPAEr4QV4l9TA==} + + '@next/eslint-plugin-next@16.2.0': + resolution: {integrity: sha512-3D3pEMcGKfENC9Pzlkr67GOm+205+5hRdYPZvHuNIy5sr9k0ybSU8g+sxOO/R/RLEh/gWZ3UlY+5LmEyZ1xgXQ==} + '@next/swc-darwin-arm64@16.1.6': resolution: {integrity: sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] + '@next/swc-darwin-arm64@16.2.0': + resolution: {integrity: sha512-/JZsqKzKt01IFoiLLAzlNqys7qk2F3JkcUhj50zuRhKDQkZNOz9E5N6wAQWprXdsvjRP4lTFj+/+36NSv5AwhQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-x64@16.1.6': resolution: {integrity: sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] + '@next/swc-darwin-x64@16.2.0': + resolution: {integrity: sha512-/hV8erWq4SNlVgglUiW5UmQ5Hwy5EW/AbbXlJCn6zkfKxTy/E/U3V8U1Ocm2YCTUoFgQdoMxRyRMOW5jYy4ygg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-linux-arm64-gnu@16.1.6': resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==} engines: {node: '>= 10'} @@ -1381,6 +1468,13 @@ packages: os: [linux] libc: [glibc] + '@next/swc-linux-arm64-gnu@16.2.0': + resolution: {integrity: sha512-GkjL/Q7MWOwqWR9zoxu1TIHzkOI2l2BHCf7FzeQG87zPgs+6WDh+oC9Sw9ARuuL/FUk6JNCgKRkA6rEQYadUaw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@next/swc-linux-arm64-musl@16.1.6': resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} engines: {node: '>= 10'} @@ -1388,6 +1482,13 @@ packages: os: [linux] libc: [musl] + '@next/swc-linux-arm64-musl@16.2.0': + resolution: {integrity: sha512-1ffhC6KY5qWLg5miMlKJp3dZbXelEfjuXt1qcp5WzSCQy36CV3y+JT7OC1WSFKizGQCDOcQbfkH/IjZP3cdRNA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@next/swc-linux-x64-gnu@16.1.6': resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} engines: {node: '>= 10'} @@ -1395,6 +1496,13 @@ packages: os: [linux] libc: [glibc] + '@next/swc-linux-x64-gnu@16.2.0': + resolution: {integrity: sha512-FmbDcZQ8yJRq93EJSL6xaE0KK/Rslraf8fj1uViGxg7K4CKBCRYSubILJPEhjSgZurpcPQq12QNOJQ0DRJl6Hg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@next/swc-linux-x64-musl@16.1.6': resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} engines: {node: '>= 10'} @@ -1402,18 +1510,37 @@ packages: os: [linux] libc: [musl] + '@next/swc-linux-x64-musl@16.2.0': + resolution: {integrity: sha512-HzjIHVkmGAwRbh/vzvoBWWEbb8BBZPxBvVbDQDvzHSf3D8RP/4vjw7MNLDXFF9Q1WEzeQyEj2zdxBtVAHu5Oyw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + '@next/swc-win32-arm64-msvc@16.1.6': resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] + '@next/swc-win32-arm64-msvc@16.2.0': + resolution: {integrity: sha512-UMiFNQf5H7+1ZsZPxEsA064WEuFbRNq/kEXyepbCnSErp4f5iut75dBA8UeerFIG3vDaQNOfCpevnERPp2V+nA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-x64-msvc@16.1.6': resolution: {integrity: sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==} engines: {node: '>= 10'} cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@16.2.0': + resolution: {integrity: sha512-DRrNJKW+/eimrZgdhVN1uvkN1OI4j6Lpefwr44jKQ0YQzztlmOBUUzHuV5GxOMPK3nmodAYElUVCY8ZXo/IWeA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@noble/ciphers@1.3.0': resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} @@ -1438,6 +1565,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -1735,774 +1866,84 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@radix-ui/number@1.1.1': - resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + '@react-grab/cli@0.1.28': + resolution: {integrity: sha512-IE4bTeH0mCq0FBRaYRUtdiIfvO7NCv14lHS4TiTY8YNG5tPYwHnLZZtniud0ElmLIWGP95Kk6E7A6J2uDmOY+Q==} + hasBin: true - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + '@rolldown/pluginutils@1.0.0-rc.7': + resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} - '@radix-ui/react-accessible-icon@1.1.7': - resolution: {integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==} - 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 + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + cpu: [arm] + os: [android] - '@radix-ui/react-accordion@1.2.12': - resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} - 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 + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + cpu: [arm64] + os: [android] - '@radix-ui/react-alert-dialog@1.1.15': - resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} - 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 + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + cpu: [arm64] + os: [darwin] - '@radix-ui/react-arrow@1.1.7': - resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} - 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 + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + cpu: [x64] + os: [darwin] - '@radix-ui/react-aspect-ratio@1.1.7': - resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} - 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 + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + cpu: [arm64] + os: [freebsd] - '@radix-ui/react-avatar@1.1.10': - resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} - 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 + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + cpu: [x64] + os: [freebsd] - '@radix-ui/react-checkbox@1.3.3': - resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} - 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 + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + cpu: [arm] + os: [linux] + libc: [glibc] - '@radix-ui/react-collapsible@1.1.12': - resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} - 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 + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + cpu: [arm] + os: [linux] + libc: [musl] - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} - 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 + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + cpu: [arm64] + os: [linux] + libc: [musl] - '@radix-ui/react-context-menu@2.2.16': - resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} - 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 + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + libc: [glibc] - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + cpu: [loong64] + os: [linux] + libc: [musl] - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} - 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-direction@1.1.1': - resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} - 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-dropdown-menu@2.1.16': - resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} - 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-focus-guards@1.1.3': - resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-focus-scope@1.1.7': - resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} - 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-form@0.1.8': - resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==} - 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-hover-card@1.1.15': - resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==} - 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-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-label@2.1.7': - resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} - 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-menu@2.1.16': - resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} - 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-menubar@1.1.16': - resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==} - 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-navigation-menu@1.2.14': - resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} - 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-one-time-password-field@0.1.8': - resolution: {integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==} - 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-password-toggle-field@0.1.3': - resolution: {integrity: sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==} - 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==} - 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-popper@1.2.8': - resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} - 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-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} - 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-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} - 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-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} - 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-progress@1.1.7': - resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} - 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-radio-group@1.3.8': - resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} - 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-roving-focus@1.1.11': - resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} - 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-scroll-area@1.2.10': - resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} - 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-select@2.2.6': - resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} - 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-separator@1.1.7': - resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} - 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-slider@1.3.6': - resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==} - 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-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-switch@1.2.6': - resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} - 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-tabs@1.1.13': - resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} - 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-toast@1.2.15': - resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} - 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-toggle-group@1.1.11': - resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} - 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-toggle@1.1.10': - resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} - 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-toolbar@1.1.11': - resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} - 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-tooltip@1.2.8': - resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} - 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-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-is-hydrated@0.1.0': - resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-previous@1.1.1': - resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.1': - resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.2.3': - resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} - 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/rect@1.1.1': - resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - - '@react-grab/cli@0.1.28': - resolution: {integrity: sha512-IE4bTeH0mCq0FBRaYRUtdiIfvO7NCv14lHS4TiTY8YNG5tPYwHnLZZtniud0ElmLIWGP95Kk6E7A6J2uDmOY+Q==} - hasBin: true - - '@rolldown/pluginutils@1.0.0-rc.7': - resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} - - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} @@ -2582,6 +2023,9 @@ packages: '@rrweb/utils@2.0.0-alpha.20': resolution: {integrity: sha512-MTQOmhPRe39C0fYaCnnVYOufQsyGzwNXpUStKiyFSfGLUJrzuwhbRoUAKR5w6W2j5XuA0bIz3ZDIBztkquOhLw==} + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -2795,6 +2239,9 @@ packages: '@tsconfig/svelte@1.0.13': resolution: {integrity: sha512-5lYJP45Xllo4yE/RUBccBT32eBlRDbqN8r1/MIvQbKxW3aFqaYPCNgm8D5V20X4ShHcwvYWNlKg3liDh1MlBoA==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -2810,6 +2257,12 @@ packages: '@types/ini@4.1.1': resolution: {integrity: sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -2841,6 +2294,65 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@typescript-eslint/eslint-plugin@8.57.2': + resolution: {integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.57.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.57.2': + resolution: {integrity: sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==} + 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.0.0' + + '@typescript-eslint/project-service@8.57.2': + resolution: {integrity: sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.57.2': + resolution: {integrity: sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.57.2': + resolution: {integrity: sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.57.2': + resolution: {integrity: sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==} + 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.0.0' + + '@typescript-eslint/types@8.57.2': + resolution: {integrity: sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.57.2': + resolution: {integrity: sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.57.2': + resolution: {integrity: sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==} + 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.0.0' + + '@typescript-eslint/visitor-keys@8.57.2': + resolution: {integrity: sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260319.1': resolution: {integrity: sha512-CMpCsOVUJ/BLC9x9VaT/DQviTmfHqw/NVwnyAR6ZHVVR1YJynJ6EJBPPHI4oStgTwVSdhna5uTPRiKhUpzvgCA==} cpu: [arm64] @@ -2880,6 +2392,109 @@ packages: resolution: {integrity: sha512-K9evb5u4QmH3Xv2XUg9OWUETYMrIX1C7Hls1ce8DW+Nlbb26NnQ5SPQCt8fGq4FGqZ9BodMwfane1pTd+BWYwQ==} hasBin: true + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + '@vitejs/plugin-react@6.0.1': resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3067,6 +2682,16 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -3079,6 +2704,9 @@ packages: ajv: optional: true + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -3116,29 +2744,83 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-hidden@1.2.6: - resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} - engines: {node: '>=10'} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + ast-types@0.16.1: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + auto-bind@5.0.1: resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.11.1: + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + babel-plugin-react-compiler@1.0.0: resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -3165,6 +2847,9 @@ packages: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@5.0.4: resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} engines: {node: 18 || 20 || >=22} @@ -3194,6 +2879,10 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -3205,6 +2894,10 @@ packages: caniuse-lite@1.0.30001778: resolution: {integrity: sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -3280,6 +2973,9 @@ packages: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-disposition@1.0.1: resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} engines: {node: '>=18'} @@ -3332,10 +3028,33 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -3353,6 +3072,9 @@ packages: babel-plugin-macros: optional: true + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -3365,10 +3087,18 @@ packages: resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} @@ -3389,9 +3119,6 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - detect-terminal@2.0.0: resolution: {integrity: sha512-94Pxgtl45fB4DAfC/dmSNQglU0En4iAmMm5kn8iycZ3lnxWBtWpW622T7WkPEomN9rn7P8LDQbQjPIoyerZW0g==} @@ -3403,6 +3130,10 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dotenv@17.3.1: resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} engines: {node: '>=12'} @@ -3430,6 +3161,9 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -3453,6 +3187,10 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -3461,6 +3199,10 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-iterator-helpers@1.3.1: + resolution: {integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==} + engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -3468,6 +3210,18 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + es-toolkit@1.45.1: resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} @@ -3481,22 +3235,146 @@ packages: engines: {node: '>=18'} hasBin: true - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@16.2.0: + resolution: {integrity: sha512-LlVJrWnjIkgQRECjIOELyAtrWFqzn326ARS5ap7swc1YKL4wkry6/gszn6wi5ZDWKxKe7fanxArvhqMoAzbL7w==} + peerDependencies: + eslint: '>=9.0.0' + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + 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} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + 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} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} + 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.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -3537,10 +3415,20 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + 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-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -3567,6 +3455,10 @@ packages: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -3582,6 +3474,21 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -3590,8 +3497,8 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} - framer-motion@12.36.0: - resolution: {integrity: sha512-4PqYHAT7gev0ke0wos+PyrcFxI0HScjm3asgU8nSYa8YzJFuwgIvdj3/s3ZaxLq0bUSboIn19A2WS/MHwLCvfw==} + framer-motion@12.38.0: + resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -3633,12 +3540,23 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + fuzzysort@3.1.0: resolution: {integrity: sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==} fzf@0.5.2: resolution: {integrity: sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==} + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3655,10 +3573,6 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - get-own-enumerable-keys@1.0.0: resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} engines: {node: '>=14.16'} @@ -3679,6 +3593,10 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} @@ -3686,6 +3604,22 @@ packages: 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'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -3701,14 +3635,33 @@ packages: resolution: {integrity: sha512-gGgrVCoDKlIZ8fIqXBBb0pPKqDgki0Z/FSKNiQzSGj2uEYHr1tq5wmBegGwJx6QB5S5cM0khSBpi/JFHMCvsmQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + has-flag@5.0.1: resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} engines: {node: '>=12'} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -3716,6 +3669,12 @@ packages: headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hono@4.12.7: resolution: {integrity: sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==} engines: {node: '>=16.9.0'} @@ -3756,6 +3715,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + indent-string@5.0.0: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} @@ -3793,6 +3756,10 @@ packages: react-devtools-core: optional: true + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + ioredis@5.10.0: resolution: {integrity: sha512-HVBe9OFuqs+Z6n64q09PQvP1/R4Bm+30PAyyD4wIEqssh3v9L21QjCVk4kRLucMBcDokJTcLjsGeVRlq/nH6DA==} engines: {node: '>=12.22.0'} @@ -3805,9 +3772,44 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3817,6 +3819,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -3825,6 +3831,10 @@ packages: resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -3847,9 +3857,21 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -3865,10 +3887,22 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + is-regexp@3.1.0: resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} engines: {node: '>=12'} + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -3877,10 +3911,22 @@ packages: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -3889,6 +3935,18 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -3897,6 +3955,9 @@ packages: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -3904,6 +3965,10 @@ packages: resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} engines: {node: '>=18'} + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -3927,9 +3992,15 @@ packages: engines: {node: '>=6'} hasBin: true + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -3939,6 +4010,13 @@ packages: json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -3953,6 +4031,13 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -3964,6 +4049,17 @@ packages: kubernetes-types@1.30.0: resolution: {integrity: sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==} + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + libsql@0.5.22: resolution: {integrity: sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA==} cpu: [x64, arm64, wasm32, arm] @@ -4124,12 +4220,19 @@ packages: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -4137,6 +4240,10 @@ packages: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -4145,6 +4252,11 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lucide-react@0.577.0: + resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} + 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==} @@ -4196,20 +4308,23 @@ packages: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - motion-dom@12.36.0: - resolution: {integrity: sha512-Ep1pq8P88rGJ75om8lTCA13zqd7ywPGwCqwuWwin6BKc0hMLkVfcS6qKlRqEo2+t0DwoUcgGJfXwaiFn4AOcQA==} + motion-dom@12.38.0: + resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} motion-utils@12.36.0: resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} - motion@12.36.0: - resolution: {integrity: sha512-5BMQuktYUX8aEByKWYx5tR4X3G08H2OMgp46wTxZ4o7CDDstyy4A0fe9RLNMjZiwvntCWGDvs16sC87/emz4Yw==} + motion@12.38.0: + resolution: {integrity: sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -4262,6 +4377,14 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -4293,11 +4416,36 @@ packages: sass: optional: true + next@16.2.0: + resolution: {integrity: sha512-NLBVrJy1pbV1Yn00L5sU4vFyAHt5XuSjzrNyFnxo6Com0M0KrL6hHM5B99dbqXb2bE9pm4Ow3Zl1xp6HVY9edQ==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} deprecated: Use your platform's native DOMException instead + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4325,10 +4473,34 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + object-treeify@1.1.33: resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} engines: {node: '>= 10'} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} @@ -4351,6 +4523,10 @@ packages: resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} engines: {node: '>=20'} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + ora@8.2.0: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} @@ -4361,6 +4537,10 @@ packages: outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + oxfmt@0.40.0: resolution: {integrity: sha512-g0C3I7xUj4b4DcagevM9kgH6+pUHytikxUcn3/VUkvzTNaaXBeyZqb7IBsHwojeXm4mTBEC/aBjBTMVUkZwWUQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4388,10 +4568,18 @@ packages: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -4441,6 +4629,9 @@ packages: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -4491,6 +4682,10 @@ packages: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postcss-selector-parser@7.1.1: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} @@ -4507,6 +4702,10 @@ packages: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -4520,10 +4719,17 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + pure-rand@8.1.0: resolution: {integrity: sha512-53B3MB8wetRdD6JZ4W/0gDKaOvKwuXrEmV1auQc0hASWge8rieKV4PCCVNVbJ+i24miiubb4c/B+dg8Ho0ikYw==} @@ -4537,19 +4743,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - radix-ui@1.4.3: - resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==} - 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 - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -4577,42 +4770,15 @@ packages: react: optional: true + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-reconciler@0.33.0: resolution: {integrity: sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==} engines: {node: '>=0.10.0'} peerDependencies: react: ^19.2.0 - react-remove-scroll-bar@2.3.8: - resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - - react-remove-scroll@2.7.2: - resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - react-style-singleton@2.2.3: - resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - react@19.2.3: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} @@ -4637,6 +4803,14 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4659,6 +4833,16 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4702,6 +4886,18 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -4735,6 +4931,18 @@ packages: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -4742,6 +4950,10 @@ packages: resolution: {integrity: sha512-jPRx44e+eyeV7xwY3BLJXcfrks00+M0h5BGB9l6DdcBW4BpAj4x3lVmVy0TXPEs2iHEisxejr62sZAAw6B1EVA==} hasBin: true + shadcn@4.1.0: + resolution: {integrity: sha512-3zETJ+0Ezj69FS6RL0HOkLKKAR5yXisXx1iISJdfLQfrUqj/VIQlanQi1Ukk+9OE+XHZVj4FQNTBSfbr2CyCYg==} + hasBin: true + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4816,6 +5028,9 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -4834,6 +5049,10 @@ packages: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} @@ -4849,6 +5068,29 @@ packages: resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} engines: {node: '>=20'} + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + stringify-object@5.0.0: resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} engines: {node: '>=14.16'} @@ -4873,6 +5115,10 @@ packages: resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} engines: {node: '>=18'} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -4890,10 +5136,18 @@ packages: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + supports-hyperlinks@4.4.0: resolution: {integrity: sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==} engines: {node: '>=20'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + supports-terminal-graphics@0.1.0: resolution: {integrity: sha512-+KdfozhS0Fw8y5Sghw8kkZNGT8nWYzJ1EzcoIvVjxhl+26TJTs26y02yfBgvc1jh5AS/c8jcI3xtahhR95KRyQ==} engines: {node: '>=20'} @@ -4966,6 +5220,23 @@ packages: toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + torph@0.0.9: + resolution: {integrity: sha512-WrFMtJwqXCfIXbLNuTOwHWff0XVm/Ewctb+71bFis3HykPvCXl1CHXapU0r67pQhAI323fsA1L2pGPeqxXeRRA==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + svelte: '>=5' + vue: '>=3' + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + svelte: + optional: true + vue: + optional: true + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -4974,9 +5245,18 @@ packages: resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + ts-morph@26.0.0: resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -5026,6 +5306,10 @@ packages: tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + type-fest@5.4.4: resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} engines: {node: '>=20'} @@ -5034,11 +5318,38 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.57.2: + resolution: {integrity: sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==} + 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.0.0' + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -5062,6 +5373,9 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + until-async@3.0.2: resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} @@ -5071,25 +5385,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - use-callback-ref@1.3.3: - resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - use-sidecar@1.1.3: - resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} @@ -5156,10 +5453,43 @@ packages: yaml: optional: true + web-haptics@0.0.6: + resolution: {integrity: sha512-eCzcf1LDi20+Fr0x9V3OkX92k0gxEQXaHajmhXHitsnk6SxPeshv8TBtBRqxyst8HI1uf2FyFVE7QS3jo1gkrw==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + svelte: '>=4' + vue: '>=3' + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + svelte: + optional: true + vue: + optional: true + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -5174,6 +5504,10 @@ packages: resolution: {integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==} engines: {node: '>=20'} + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -5225,6 +5559,10 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + yoctocolors-cjs@2.1.3: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} @@ -5241,6 +5579,12 @@ packages: peerDependencies: zod: ^3.25 || ^4 + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -5493,27 +5837,27 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@base-ui/react@1.3.0(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@base-ui/react@1.3.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@babel/runtime': 7.28.6 - '@base-ui/utils': 0.2.6(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@base-ui/utils': 0.2.6(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@floating-ui/utils': 0.2.11 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) tabbable: 6.4.0 - use-sync-external-store: 1.6.0(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.4) optionalDependencies: '@types/react': 19.2.14 - '@base-ui/utils@0.2.6(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@base-ui/utils@0.2.6(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@babel/runtime': 7.28.6 '@floating-ui/utils': 0.2.11 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.4) optionalDependencies: '@types/react': 19.2.14 @@ -5702,11 +6046,22 @@ snapshots: - bufferutil - utf-8-validate + '@emnapi/core@1.9.1': + dependencies: + '@emnapi/wasi-threads': 1.2.0 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.9.0': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.0': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -5863,6 +6218,52 @@ snapshots: '@esbuild/win32-x64@0.27.4': optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': + dependencies: + eslint: 9.39.4(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.14.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.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 @@ -5872,11 +6273,11 @@ snapshots: '@floating-ui/core': 1.7.5 '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@floating-ui/react-dom@2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@floating-ui/dom': 1.7.6 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) '@floating-ui/utils@0.2.11': {} @@ -5884,6 +6285,17 @@ snapshots: dependencies: hono: 4.12.7 + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@img/colour@1.1.0': optional: true @@ -6185,34 +6597,71 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.9.1 + '@emnapi/runtime': 1.9.0 + '@tybys/wasm-util': 0.10.1 + optional: true + '@neon-rs/load@0.0.4': {} '@next/env@16.1.6': {} + '@next/env@16.2.0': {} + + '@next/eslint-plugin-next@16.2.0': + dependencies: + fast-glob: 3.3.1 + '@next/swc-darwin-arm64@16.1.6': optional: true + '@next/swc-darwin-arm64@16.2.0': + optional: true + '@next/swc-darwin-x64@16.1.6': optional: true + '@next/swc-darwin-x64@16.2.0': + optional: true + '@next/swc-linux-arm64-gnu@16.1.6': optional: true + '@next/swc-linux-arm64-gnu@16.2.0': + optional: true + '@next/swc-linux-arm64-musl@16.1.6': optional: true + '@next/swc-linux-arm64-musl@16.2.0': + optional: true + '@next/swc-linux-x64-gnu@16.1.6': optional: true + '@next/swc-linux-x64-gnu@16.2.0': + optional: true + '@next/swc-linux-x64-musl@16.1.6': optional: true + '@next/swc-linux-x64-musl@16.2.0': + optional: true + '@next/swc-win32-arm64-msvc@16.1.6': optional: true + '@next/swc-win32-arm64-msvc@16.2.0': + optional: true + '@next/swc-win32-x64-msvc@16.1.6': optional: true + '@next/swc-win32-x64-msvc@16.2.0': + optional: true + '@noble/ciphers@1.3.0': {} '@noble/curves@1.9.7': @@ -6233,6 +6682,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@nolyfill/is-core-module@1.0.39': {} + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': @@ -6255,880 +6706,133 @@ snapshots: '@oxfmt/binding-android-arm64@0.40.0': optional: true - '@oxfmt/binding-darwin-arm64@0.40.0': - optional: true - - '@oxfmt/binding-darwin-x64@0.40.0': - optional: true - - '@oxfmt/binding-freebsd-x64@0.40.0': - optional: true - - '@oxfmt/binding-linux-arm-gnueabihf@0.40.0': - optional: true - - '@oxfmt/binding-linux-arm-musleabihf@0.40.0': - optional: true - - '@oxfmt/binding-linux-arm64-gnu@0.40.0': - optional: true - - '@oxfmt/binding-linux-arm64-musl@0.40.0': - optional: true - - '@oxfmt/binding-linux-ppc64-gnu@0.40.0': - optional: true - - '@oxfmt/binding-linux-riscv64-gnu@0.40.0': - optional: true - - '@oxfmt/binding-linux-riscv64-musl@0.40.0': - optional: true - - '@oxfmt/binding-linux-s390x-gnu@0.40.0': - optional: true - - '@oxfmt/binding-linux-x64-gnu@0.40.0': - optional: true - - '@oxfmt/binding-linux-x64-musl@0.40.0': - optional: true - - '@oxfmt/binding-openharmony-arm64@0.40.0': - optional: true - - '@oxfmt/binding-win32-arm64-msvc@0.40.0': - optional: true - - '@oxfmt/binding-win32-ia32-msvc@0.40.0': - optional: true - - '@oxfmt/binding-win32-x64-msvc@0.40.0': - optional: true - - '@oxlint-tsgolint/darwin-arm64@0.17.0': - optional: true - - '@oxlint-tsgolint/darwin-x64@0.17.0': - optional: true - - '@oxlint-tsgolint/linux-arm64@0.17.0': - optional: true - - '@oxlint-tsgolint/linux-x64@0.17.0': - optional: true - - '@oxlint-tsgolint/win32-arm64@0.17.0': - optional: true - - '@oxlint-tsgolint/win32-x64@0.17.0': - optional: true - - '@oxlint/binding-android-arm-eabi@1.55.0': - optional: true - - '@oxlint/binding-android-arm64@1.55.0': - optional: true - - '@oxlint/binding-darwin-arm64@1.55.0': - optional: true - - '@oxlint/binding-darwin-x64@1.55.0': - optional: true - - '@oxlint/binding-freebsd-x64@1.55.0': - optional: true - - '@oxlint/binding-linux-arm-gnueabihf@1.55.0': - optional: true - - '@oxlint/binding-linux-arm-musleabihf@1.55.0': - optional: true - - '@oxlint/binding-linux-arm64-gnu@1.55.0': - optional: true - - '@oxlint/binding-linux-arm64-musl@1.55.0': - optional: true - - '@oxlint/binding-linux-ppc64-gnu@1.55.0': - optional: true - - '@oxlint/binding-linux-riscv64-gnu@1.55.0': - optional: true - - '@oxlint/binding-linux-riscv64-musl@1.55.0': - optional: true - - '@oxlint/binding-linux-s390x-gnu@1.55.0': - optional: true - - '@oxlint/binding-linux-x64-gnu@1.55.0': - optional: true - - '@oxlint/binding-linux-x64-musl@1.55.0': - optional: true - - '@oxlint/binding-openharmony-arm64@1.55.0': - optional: true - - '@oxlint/binding-win32-arm64-msvc@1.55.0': - optional: true - - '@oxlint/binding-win32-ia32-msvc@1.55.0': + '@oxfmt/binding-darwin-arm64@0.40.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.55.0': + '@oxfmt/binding-darwin-x64@0.40.0': optional: true - '@polka/url@1.0.0-next.29': {} - - '@radix-ui/number@1.1.1': {} + '@oxfmt/binding-freebsd-x64@0.40.0': + optional: true - '@radix-ui/primitive@1.1.3': {} + '@oxfmt/binding-linux-arm-gnueabihf@0.40.0': + optional: true - '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@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.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-linux-arm-musleabihf@0.40.0': + optional: true - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-linux-arm64-gnu@0.40.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-linux-arm64-musl@0.40.0': + optional: true - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-linux-ppc64-gnu@0.40.0': + optional: true - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - 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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-linux-riscv64-gnu@0.40.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-linux-riscv64-musl@0.40.0': + optional: true - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxfmt/binding-linux-s390x-gnu@0.40.0': + optional: true - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-linux-x64-gnu@0.40.0': + optional: true - '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxfmt/binding-linux-x64-musl@0.40.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - aria-hidden: 1.2.6 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-openharmony-arm64@0.40.0': + optional: true - '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxfmt/binding-win32-arm64-msvc@0.40.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-win32-ia32-msvc@0.40.0': + optional: true - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxfmt/binding-win32-x64-msvc@0.40.0': + optional: true - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint-tsgolint/darwin-arm64@0.17.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint-tsgolint/darwin-x64@0.17.0': + optional: true - '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint-tsgolint/linux-arm64@0.17.0': + optional: true - '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint-tsgolint/linux-x64@0.17.0': + optional: true - '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - aria-hidden: 1.2.6 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@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.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/number': 1.1.1 - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - 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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - aria-hidden: 1.2.6 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.3) - 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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/rect': 1.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint-tsgolint/win32-arm64@0.17.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint-tsgolint/win32-x64@0.17.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-android-arm-eabi@1.55.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-android-arm64@1.55.0': + optional: true - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - 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.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - 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.3(react@19.2.3))(react@19.2.3)': - 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.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/number': 1.1.1 - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - aria-hidden: 1.2.6 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-darwin-arm64@1.55.0': + optional: true - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/number': 1.1.1 - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-darwin-x64@1.55.0': + optional: true - '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.3)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-freebsd-x64@1.55.0': + optional: true - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@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.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-linux-arm-gnueabihf@1.55.0': + optional: true - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-linux-arm-musleabihf@1.55.0': + optional: true - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-linux-arm64-gnu@1.55.0': + optional: true - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-linux-arm64-musl@1.55.0': + optional: true - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-linux-ppc64-gnu@1.55.0': + optional: true - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-linux-riscv64-gnu@1.55.0': + optional: true - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-linux-riscv64-musl@1.55.0': + optional: true - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-linux-s390x-gnu@1.55.0': + optional: true - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - use-sync-external-store: 1.6.0(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-linux-x64-gnu@1.55.0': + optional: true - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-linux-x64-musl@1.55.0': + optional: true - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-openharmony-arm64@1.55.0': + optional: true - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-win32-arm64-msvc@1.55.0': + optional: true - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.14 + '@oxlint/binding-win32-ia32-msvc@1.55.0': + optional: true - '@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.3(react@19.2.3))(react@19.2.3)': - 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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@oxlint/binding-win32-x64-msvc@1.55.0': + optional: true - '@radix-ui/rect@1.1.1': {} + '@polka/url@1.0.0-next.29': {} '@react-grab/cli@0.1.28': dependencies: @@ -7232,6 +6936,8 @@ snapshots: '@rrweb/utils@2.0.0-alpha.20': {} + '@rtsao/scc@1.1.0': {} + '@sec-ant/readable-stream@0.4.1': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -7394,6 +7100,11 @@ snapshots: '@tsconfig/svelte@1.0.13': {} + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -7407,6 +7118,10 @@ snapshots: '@types/ini@4.1.1': {} + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + '@types/node@12.20.55': {} '@types/node@20.19.37': @@ -7437,6 +7152,97 @@ snapshots: dependencies: '@types/node': 22.19.15 + '@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.57.2 + '@typescript-eslint/type-utils': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.2 + eslint: 9.39.4(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.57.2 + '@typescript-eslint/types': 8.57.2 + '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.2 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.57.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.57.2(typescript@5.9.3) + '@typescript-eslint/types': 8.57.2 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.57.2': + dependencies: + '@typescript-eslint/types': 8.57.2 + '@typescript-eslint/visitor-keys': 8.57.2 + + '@typescript-eslint/tsconfig-utils@8.57.2(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.57.2 + '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.6.1) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.57.2': {} + + '@typescript-eslint/typescript-estree@8.57.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.57.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.57.2(typescript@5.9.3) + '@typescript-eslint/types': 8.57.2 + '@typescript-eslint/visitor-keys': 8.57.2 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.57.2 + '@typescript-eslint/types': 8.57.2 + '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) + eslint: 9.39.4(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.57.2': + dependencies: + '@typescript-eslint/types': 8.57.2 + eslint-visitor-keys: 5.0.1 + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260319.1': optional: true @@ -7468,6 +7274,65 @@ snapshots: '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260319.1 '@typescript/native-preview-win32-x64': 7.0.0-dev.20260319.1 + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + '@vitejs/plugin-react@6.0.1(@voidzero-dev/vite-plus-core@0.1.12(@types/node@22.19.15)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(babel-plugin-react-compiler@1.0.0)': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 @@ -7588,12 +7453,25 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + agent-base@7.1.4: {} ajv-formats@3.0.1(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 + ajv@6.14.0: + 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@8.18.0: dependencies: fast-deep-equal: 3.1.3 @@ -7625,24 +7503,103 @@ snapshots: argparse@2.0.1: {} - aria-hidden@1.2.6: + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: dependencies: - tslib: 2.8.1 + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 array-union@2.1.0: {} + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + assertion-error@2.0.1: {} + ast-types-flow@0.0.8: {} + ast-types@0.16.1: dependencies: tslib: 2.8.1 + async-function@1.0.0: {} + auto-bind@5.0.1: {} + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.11.1: {} + + axobject-query@4.1.0: {} + babel-plugin-react-compiler@1.0.0: dependencies: '@babel/types': 7.29.0 + balanced-match@1.0.2: {} + balanced-match@4.0.4: {} base64-arraybuffer@1.0.2: {} @@ -7653,10 +7610,10 @@ snapshots: dependencies: is-windows: 1.0.2 - bippy@0.5.32(@types/react@19.2.14)(react@19.2.3): + bippy@0.5.32(@types/react@19.2.14)(react@19.2.4): dependencies: '@types/react-reconciler': 0.28.9(@types/react@19.2.14) - react: 19.2.3 + react: 19.2.4 transitivePeerDependencies: - '@types/react' @@ -7674,6 +7631,11 @@ snapshots: transitivePeerDependencies: - supports-color + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@5.0.4: dependencies: balanced-match: 4.0.4 @@ -7703,6 +7665,13 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -7712,6 +7681,11 @@ snapshots: caniuse-lite@1.0.30001778: {} + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@5.6.2: {} chardet@2.1.1: {} @@ -7769,6 +7743,8 @@ snapshots: commander@14.0.3: {} + concat-map@0.0.1: {} + content-disposition@1.0.1: {} content-type@1.0.5: {} @@ -7807,14 +7783,40 @@ snapshots: csstype@3.2.3: {} + damerau-levenshtein@1.0.8: {} + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + debug@4.4.3: dependencies: ms: 2.1.3 dedent@1.7.2: {} + deep-is@0.1.4: {} + deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -7824,8 +7826,20 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.1 + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + denque@2.1.0: {} depd@2.0.0: {} @@ -7836,15 +7850,17 @@ snapshots: detect-libc@2.1.2: {} - detect-node-es@1.1.0: {} - detect-terminal@2.0.0: {} diff@8.0.3: {} dir-glob@3.0.1: dependencies: - path-type: 4.0.0 + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 dotenv@17.3.1: {} @@ -7882,6 +7898,8 @@ snapshots: emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + encodeurl@2.0.0: {} enhanced-resolve@5.20.0: @@ -7902,16 +7920,110 @@ snapshots: dependencies: is-arrayish: 0.2.1 + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + es-define-property@1.0.1: {} es-errors@1.3.0: {} + es-iterator-helpers@1.3.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + safe-array-concat: 1.1.3 + es-module-lexer@1.7.0: {} es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + es-toolkit@1.45.1: {} esbuild@0.25.12: @@ -7978,8 +8090,215 @@ snapshots: escape-string-regexp@2.0.0: {} + escape-string-regexp@4.0.0: {} + + eslint-config-next@16.2.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 16.2.0 + eslint: 9.39.4(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1)) + globals: 16.4.0 + typescript-eslint: 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.6.1) + get-tsconfig: 4.13.6 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.4(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.6.1)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.1 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.4(jiti@2.6.1) + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + eslint: 9.39.4(jiti@2.6.1) + hermes-parser: 0.25.1 + zod: 4.3.6 + zod-validation-error: 4.0.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.6.1)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 9.39.4(jiti@2.6.1) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + 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.4(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + 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 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + 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.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + esprima@4.0.1: {} + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + etag@1.8.1: {} eventsource-parser@3.0.6: {} @@ -8061,6 +8380,14 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-glob@3.3.1: + 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-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8069,6 +8396,10 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} fastq@1.20.1: @@ -8090,6 +8421,10 @@ snapshots: dependencies: is-unicode-supported: 2.1.0 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -8112,20 +8447,36 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 forwarded@0.2.0: {} - framer-motion@12.36.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + framer-motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - motion-dom: 12.36.0 + motion-dom: 12.38.0 motion-utils: 12.36.0 tslib: 2.8.1 optionalDependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) fresh@2.0.0: {} @@ -8155,10 +8506,23 @@ snapshots: function-bind@1.1.2: {} + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + fuzzysort@3.1.0: {} fzf@0.5.2: {} + generator-function@2.0.1: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -8178,8 +8542,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-nonce@1.0.1: {} - get-own-enumerable-keys@1.0.0: {} get-port@7.1.0: {} @@ -8196,15 +8558,33 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + get-tsconfig@4.13.6: dependencies: resolve-pkg-maps: 1.0.0 - optional: true glob-parent@5.1.2: dependencies: is-glob: 4.0.3 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.4.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -8220,16 +8600,38 @@ snapshots: graphql@16.13.1: {} + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + has-flag@5.0.1: {} + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 headers-polyfill@4.0.3: {} + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + hono@4.12.7: {} http-errors@2.0.1: @@ -8266,6 +8668,8 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + imurmurhash@0.1.4: {} + indent-string@5.0.0: {} inherits@2.0.4: {} @@ -8317,6 +8721,12 @@ snapshots: - bufferutil - utf-8-validate + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + ioredis@5.10.0: dependencies: '@ioredis/commands': 1.5.1 @@ -8335,18 +8745,74 @@ snapshots: ipaddr.js@1.9.1: {} + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.7.4 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-docker@3.0.0: {} is-extglob@2.1.1: {} + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} is-fullwidth-code-point@5.1.0: dependencies: get-east-asian-width: 1.5.0 + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -8361,8 +8827,17 @@ snapshots: is-interactive@2.0.0: {} + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + is-node-process@1.2.0: {} + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-number@7.0.0: {} is-obj@3.0.0: {} @@ -8371,30 +8846,80 @@ snapshots: is-promise@4.0.0: {} + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + is-regexp@3.1.0: {} + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + is-stream@2.0.1: {} is-stream@4.0.1: {} + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + is-unicode-supported@1.3.0: {} is-unicode-supported@2.1.0: {} + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-windows@1.0.2: {} is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 + isarray@2.0.5: {} + isexe@2.0.0: {} isexe@3.1.5: {} + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + jiti@2.6.1: {} jose@6.2.1: {} @@ -8412,14 +8937,24 @@ snapshots: jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} json-schema-typed@8.0.2: {} json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + json5@2.2.3: {} jsonc-parser@3.3.1: {} @@ -8434,12 +8969,34 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + kleur@3.0.3: {} kleur@4.1.5: {} kubernetes-types@1.30.0: {} + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + libsql@0.5.22: dependencies: '@neon-rs/load': 0.0.4 @@ -8559,10 +9116,16 @@ snapshots: dependencies: p-locate: 4.1.0 + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + lodash.defaults@4.2.0: {} lodash.isarguments@3.1.0: {} + lodash.merge@4.6.2: {} + lodash.startcase@4.4.0: {} log-symbols@6.0.0: @@ -8570,15 +9133,19 @@ snapshots: chalk: 5.6.2 is-unicode-supported: 1.3.0 + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-react@0.576.0(react@19.2.3): + lucide-react@0.576.0(react@19.2.4): dependencies: - react: 19.2.3 + react: 19.2.4 - lucide-react@0.576.0(react@19.2.4): + lucide-react@0.577.0(react@19.2.4): dependencies: react: 19.2.4 @@ -8617,23 +9184,27 @@ snapshots: dependencies: brace-expansion: 5.0.4 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + minimist@1.2.8: {} mitt@3.0.1: {} - motion-dom@12.36.0: + motion-dom@12.38.0: dependencies: motion-utils: 12.36.0 motion-utils@12.36.0: {} - motion@12.36.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - framer-motion: 12.36.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + framer-motion: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) tslib: 2.8.1 optionalDependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) mri@1.2.0: {} @@ -8713,14 +9284,18 @@ snapshots: nanoid@3.3.11: {} + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + negotiator@1.0.0: {} - next-themes@0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + next-themes@0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) - next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@next/env': 16.1.6 '@swc/helpers': 0.5.15 @@ -8729,7 +9304,7 @@ snapshots: postcss: 8.4.31 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.3) + styled-jsx: 5.1.6(react@19.2.3) optionalDependencies: '@next/swc-darwin-arm64': 16.1.6 '@next/swc-darwin-x64': 16.1.6 @@ -8746,8 +9321,41 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@16.2.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@next/env': 16.2.0 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.10.7 + caniuse-lite: 1.0.30001778 + postcss: 8.4.31 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) + optionalDependencies: + '@next/swc-darwin-arm64': 16.2.0 + '@next/swc-darwin-x64': 16.2.0 + '@next/swc-linux-arm64-gnu': 16.2.0 + '@next/swc-linux-arm64-musl': 16.2.0 + '@next/swc-linux-x64-gnu': 16.2.0 + '@next/swc-linux-x64-musl': 16.2.0 + '@next/swc-win32-arm64-msvc': 16.2.0 + '@next/swc-win32-x64-msvc': 16.2.0 + '@opentelemetry/api': 1.9.0 + babel-plugin-react-compiler: 1.0.0 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + node-domexception@1.0.0: {} + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 @@ -8774,8 +9382,46 @@ snapshots: object-inspect@1.13.4: {} + object-keys@1.1.1: {} + object-treeify@1.1.33: {} + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + obug@2.1.1: {} on-finished@2.4.1: @@ -8803,6 +9449,15 @@ snapshots: powershell-utils: 0.1.0 wsl-utils: 0.3.1 + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + ora@8.2.0: dependencies: chalk: 5.6.2 @@ -8819,6 +9474,12 @@ snapshots: outvariant@1.4.3: {} + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + oxfmt@0.40.0: dependencies: tinypool: 2.1.0 @@ -8883,10 +9544,18 @@ snapshots: dependencies: p-try: 2.2.0 + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + p-locate@4.1.0: dependencies: p-limit: 2.3.0 + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + p-map@2.1.0: {} p-try@2.2.0: {} @@ -8922,6 +9591,8 @@ snapshots: path-key@4.0.0: {} + path-parse@1.0.7: {} + path-to-regexp@6.3.0: {} path-to-regexp@8.3.0: {} @@ -8954,6 +9625,8 @@ snapshots: pngjs@7.0.0: {} + possible-typed-array-names@1.1.0: {} + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 @@ -8973,6 +9646,8 @@ snapshots: powershell-utils@0.1.0: {} + prelude-ls@1.2.1: {} + prettier@2.8.8: {} pretty-ms@9.3.0: @@ -8984,11 +9659,19 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 + punycode@2.3.1: {} + pure-rand@8.1.0: {} qs@6.15.0: @@ -8999,69 +9682,6 @@ snapshots: queue-microtask@1.2.3: {} - radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.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.3(react@19.2.3))(react@19.2.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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.3) - '@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.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - range-parser@1.2.1: {} raw-body@3.0.2: @@ -9081,49 +9701,24 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 - react-grab@0.1.28(@types/react@19.2.14)(react@19.2.3): + react-grab@0.1.28(@types/react@19.2.14)(react@19.2.4): dependencies: '@medv/finder': 4.0.2 '@react-grab/cli': 0.1.28 - bippy: 0.5.32(@types/react@19.2.14)(react@19.2.3) + bippy: 0.5.32(@types/react@19.2.14)(react@19.2.4) solid-js: 1.9.11 optionalDependencies: - react: 19.2.3 + react: 19.2.4 transitivePeerDependencies: - '@types/react' + react-is@16.13.1: {} + react-reconciler@0.33.0(react@19.2.4): dependencies: react: 19.2.4 scheduler: 0.27.0 - react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.3): - dependencies: - react: 19.2.3 - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.3) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.14 - - react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.3): - dependencies: - react: 19.2.3 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.3) - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.3) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.3) - use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - - react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.3): - dependencies: - get-nonce: 1.0.1 - react: 19.2.3 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.14 - react@19.2.3: {} react@19.2.4: {} @@ -9149,6 +9744,26 @@ snapshots: dependencies: redis-errors: 1.2.0 + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -9159,8 +9774,22 @@ snapshots: resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: - optional: true + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 restore-cursor@4.0.0: dependencies: @@ -9248,6 +9877,25 @@ snapshots: dependencies: queue-microtask: 1.2.3 + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + safer-buffer@2.1.2: {} scheduler@0.27.0: {} @@ -9287,9 +9935,31 @@ snapshots: transitivePeerDependencies: - supports-color + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + setprototypeof@1.2.0: {} - shadcn@3.8.5(@types/node@20.19.37)(typescript@5.9.3): + shadcn@3.8.5(@types/node@22.19.15)(typescript@5.9.3): dependencies: '@antfu/ni': 25.0.0 '@babel/core': 7.29.0 @@ -9311,7 +9981,7 @@ snapshots: fuzzysort: 3.1.0 https-proxy-agent: 7.0.6 kleur: 4.1.5 - msw: 2.12.10(@types/node@20.19.37)(typescript@5.9.3) + msw: 2.12.10(@types/node@22.19.15)(typescript@5.9.3) node-fetch: 3.3.2 open: 11.0.0 ora: 8.2.0 @@ -9333,9 +10003,8 @@ snapshots: - supports-color - typescript - shadcn@3.8.5(@types/node@22.19.15)(typescript@5.9.3): + shadcn@4.1.0(@types/node@20.19.37)(typescript@5.9.3): dependencies: - '@antfu/ni': 25.0.0 '@babel/core': 7.29.0 '@babel/parser': 7.29.0 '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) @@ -9355,7 +10024,7 @@ snapshots: fuzzysort: 3.1.0 https-proxy-agent: 7.0.6 kleur: 4.1.5 - msw: 2.12.10(@types/node@22.19.15)(typescript@5.9.3) + msw: 2.12.10(@types/node@20.19.37)(typescript@5.9.3) node-fetch: 3.3.2 open: 11.0.0 ora: 8.2.0 @@ -9489,6 +10158,8 @@ snapshots: sprintf-js@1.0.3: {} + stable-hash@0.0.5: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -9501,6 +10172,11 @@ snapshots: stdin-discarder@0.2.2: {} + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + strict-event-emitter@0.5.1: {} string-width@4.2.3: @@ -9520,6 +10196,56 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + stringify-object@5.0.0: dependencies: get-own-enumerable-keys: 1.0.0 @@ -9540,20 +10266,33 @@ snapshots: strip-final-newline@4.0.0: {} - styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.3): + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.4): dependencies: client-only: 0.0.1 - react: 19.2.3 + react: 19.2.4 optionalDependencies: '@babel/core': 7.29.0 + styled-jsx@5.1.6(react@19.2.3): + dependencies: + client-only: 0.0.1 + react: 19.2.3 + supports-color@10.2.2: {} + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + supports-hyperlinks@4.4.0: dependencies: has-flag: 5.0.1 supports-color: 10.2.2 + supports-preserve-symlinks-flag@1.0.0: {} + supports-terminal-graphics@0.1.0: {} tabbable@6.4.0: {} @@ -9604,17 +10343,33 @@ snapshots: toml@3.0.0: {} + torph@0.0.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + totalist@3.0.1: {} tough-cookie@6.0.1: dependencies: tldts: 7.0.25 + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + ts-morph@26.0.0: dependencies: '@ts-morph/common': 0.27.0 code-block-writer: 13.0.3 + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -9660,6 +10415,10 @@ snapshots: tw-animate-css@1.4.0: {} + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + type-fest@5.4.4: dependencies: tagged-tag: 1.0.0 @@ -9670,8 +10429,59 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@5.9.3: {} + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + undici-types@6.21.0: {} undici@7.24.4: {} @@ -9684,6 +10494,30 @@ snapshots: unpipe@1.0.0: {} + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + until-async@3.0.2: {} update-browserslist-db@1.2.3(browserslist@4.28.1): @@ -9692,29 +10526,13 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.3): - dependencies: - react: 19.2.3 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.14 - - use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.3): - dependencies: - detect-node-es: 1.1.0 - react: 19.2.3 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.2.14 - - use-sync-external-store@1.6.0(react@19.2.3): + uri-js@4.4.1: dependencies: - react: 19.2.3 + punycode: 2.3.1 use-sync-external-store@1.6.0(react@19.2.4): dependencies: react: 19.2.4 - optional: true util-deprecate@1.0.2: {} @@ -9786,8 +10604,54 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 + web-haptics@0.0.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + web-streams-polyfill@3.3.3: {} + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -9800,6 +10664,8 @@ snapshots: dependencies: string-width: 8.2.0 + word-wrap@1.2.5: {} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -9845,6 +10711,8 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yocto-queue@0.1.0: {} + yoctocolors-cjs@2.1.3: {} yoctocolors@2.1.2: {} @@ -9859,16 +10727,14 @@ snapshots: dependencies: zod: 4.3.6 + zod-validation-error@4.0.2(zod@4.3.6): + dependencies: + zod: 4.3.6 + zod@3.25.76: {} zod@4.3.6: {} - zustand@5.0.11(@types/react@19.2.14)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): - optionalDependencies: - '@types/react': 19.2.14 - react: 19.2.3 - use-sync-external-store: 1.6.0(react@19.2.3) - zustand@5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): optionalDependencies: '@types/react': 19.2.14 From 2c37b754ac05490a902adae6be47421e07ac6294 Mon Sep 17 00:00:00 2001 From: Aiden Bai Date: Tue, 24 Mar 2026 03:53:10 -0700 Subject: [PATCH 08/10] fix --- packages/browser/src/mcp/mcp-session.ts | 12 ++++++----- packages/recorder/src/live-view-server.ts | 26 +++++++++++++++++------ packages/recorder/src/replay-broadcast.ts | 5 ++++- packages/recorder/viewer/src/app.tsx | 2 +- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/browser/src/mcp/mcp-session.ts b/packages/browser/src/mcp/mcp-session.ts index 8af14d4c..ba373cb8 100644 --- a/packages/browser/src/mcp/mcp-session.ts +++ b/packages/browser/src/mcp/mcp-session.ts @@ -97,8 +97,10 @@ const flushBroadcastToFile = (broadcast: ReplayBroadcast, outputPath: string) => if (allEvents.length === 0) return; const ndjson = allEvents.map((event) => JSON.stringify(event)).join("\n") + "\n"; - mkdirSync(dirname(outputPath), { recursive: true }); - writeFileSync(outputPath, ndjson); + yield* Effect.try(() => { + mkdirSync(dirname(outputPath), { recursive: true }); + writeFileSync(outputPath, ndjson); + }); const runState = yield* broadcast.snapshotRunState; const replayFileName = basename(outputPath); @@ -109,11 +111,11 @@ const flushBroadcastToFile = (broadcast: ReplayBroadcast, outputPath: string) => eventsSource: { ndjsonPath: replayFileName }, steps: runState, }); - writeFileSync(htmlReportPath, reportHtml); + yield* Effect.try(() => writeFileSync(htmlReportPath, reportHtml)); - const runStateFilePath = join(dirname(outputPath), "run-state.json"); if (runState) { - writeFileSync(runStateFilePath, JSON.stringify(runState)); + const runStateFilePath = join(dirname(outputPath), "run-state.json"); + yield* Effect.try(() => writeFileSync(runStateFilePath, JSON.stringify(runState))); } }); diff --git a/packages/recorder/src/live-view-server.ts b/packages/recorder/src/live-view-server.ts index d0620cfe..4c6d050d 100644 --- a/packages/recorder/src/live-view-server.ts +++ b/packages/recorder/src/live-view-server.ts @@ -16,28 +16,37 @@ const drainToSse = (broadcast: ReplayBroadcast, response: ServerResponse) => Effect.gen(function* () { const snapshot = yield* broadcast.snapshotEvents; if (snapshot.length > 0) { - response.write(`event: replay\ndata: ${JSON.stringify(snapshot)}\n\n`); + yield* Effect.try(() => + response.write(`event: replay\ndata: ${JSON.stringify(snapshot)}\n\n`), + ); } const runState = yield* broadcast.snapshotRunState; if (runState) { - response.write(`event: steps\ndata: ${JSON.stringify(runState)}\n\n`); + yield* Effect.try(() => + response.write(`event: steps\ndata: ${JSON.stringify(runState)}\n\n`), + ); } const eventsQueue = yield* PubSub.subscribe(broadcast.eventsPubSub); const stepsQueue = yield* PubSub.subscribe(broadcast.runStatePubSub); + const writeSse = (event: string, data: string) => + Effect.try(() => { + response.write(`event: ${event}\ndata: ${data}\n\n`); + }); + const forwardEvents = Effect.forever( Effect.gen(function* () { const batch = yield* Queue.take(eventsQueue); - response.write(`event: replay\ndata: ${JSON.stringify(batch)}\n\n`); + yield* writeSse("replay", JSON.stringify(batch)); }), ); const forwardSteps = Effect.forever( Effect.gen(function* () { const state = yield* Queue.take(stepsQueue); - response.write(`event: steps\ndata: ${JSON.stringify(state)}\n\n`); + yield* writeSse("steps", JSON.stringify(state)); }), ); @@ -99,8 +108,13 @@ export const startLiveViewServer = Effect.fn("LiveViewServer.start")(function* ( if (request.url === "/run-state.json") { Effect.runPromise(broadcast.snapshotRunState).then((runState) => { - response.writeHead(200, { "Content-Type": "application/json" }); - response.end(JSON.stringify(runState ?? undefined)); + if (runState) { + response.writeHead(200, { "Content-Type": "application/json" }); + response.end(JSON.stringify(runState)); + } else { + response.writeHead(204); + response.end(); + } }); return; } diff --git a/packages/recorder/src/replay-broadcast.ts b/packages/recorder/src/replay-broadcast.ts index b628e7fe..5cc92b2e 100644 --- a/packages/recorder/src/replay-broadcast.ts +++ b/packages/recorder/src/replay-broadcast.ts @@ -19,7 +19,10 @@ export const makeReplayBroadcast = Effect.gen(function* () { const publishEvents = (events: readonly eventWithTime[]) => Effect.gen(function* () { - yield* Ref.update(eventsRef, (previous) => [...previous, ...events]); + yield* Ref.update(eventsRef, (previous) => { + for (const event of events) previous.push(event); + return previous; + }); yield* PubSub.publish(eventsPubSub, events); }); diff --git a/packages/recorder/viewer/src/app.tsx b/packages/recorder/viewer/src/app.tsx index 862f80fc..a64be555 100644 --- a/packages/recorder/viewer/src/app.tsx +++ b/packages/recorder/viewer/src/app.tsx @@ -83,7 +83,7 @@ export const App = () => { try { const stateResponse = await fetch("/run-state.json"); - if (stateResponse.ok) { + if (stateResponse.ok && stateResponse.status !== 204) { const state: ViewerRunState = await stateResponse.json(); if (state?.steps) setRunState(state); } From 3eae6d144b75dbc5addafdf20e89a7088c1a1122 Mon Sep 17 00:00:00 2001 From: skoshx Date: Tue, 24 Mar 2026 22:06:44 +0200 Subject: [PATCH 09/10] wip --- .specs/live-viewer-rpc.md | 440 ++++++++++++++++++ .../src/components/screens/results-screen.tsx | 2 +- .../src/components/screens/testing-screen.tsx | 113 +++-- apps/cli/src/constants.ts | 3 - apps/cli/src/index.tsx | 20 +- apps/cli/src/layers.ts | 6 +- apps/cli/src/live-view-url.ts | 6 - apps/cli/src/live-viewer-server.ts | 41 ++ packages/browser/package.json | 1 + packages/browser/src/mcp/constants.ts | 2 - packages/browser/src/mcp/index.ts | 1 - .../browser/src/mcp/live-viewer-client.ts | 23 + packages/browser/src/mcp/mcp-session.ts | 152 +----- packages/browser/src/mcp/server.ts | 4 +- packages/recorder/package.json | 11 +- packages/recorder/src/constants.ts | 4 - packages/recorder/src/index.ts | 9 - packages/recorder/src/live-view-server.ts | 149 ------ packages/recorder/src/replay-broadcast.ts | 43 -- packages/recorder/src/replay-viewer.ts | 172 ------- packages/recorder/src/viewer-events.ts | 38 -- .../recorder/tests/live-view-server.test.ts | 85 ---- packages/recorder/tests/replay-viewer.test.ts | 86 ---- packages/recorder/viewer/src/app.tsx | 131 +++--- .../recorder/viewer/src/atoms/live-updates.ts | 16 + .../viewer/src/hooks/use-mount-effect.ts | 6 - .../recorder/viewer/src/injected-events.ts | 5 + packages/recorder/viewer/src/main.tsx | 5 +- packages/recorder/viewer/src/rpc/client.ts | 25 + packages/recorder/viewer/src/steps-panel.tsx | 55 ++- packages/recorder/viewer/vite.config.ts | 45 +- packages/shared/package.json | 6 +- packages/shared/src/constants.ts | 5 + packages/shared/src/index.ts | 4 + packages/shared/src/rpc/live-viewer.rpc.ts | 31 ++ packages/shared/src/rpcs.ts | 2 + packages/shared/vite.config.ts | 3 +- packages/supervisor/src/index.ts | 2 + packages/supervisor/src/live-viewer.ts | 27 ++ .../src/rpc/live-viewer.rpc.layer.ts | 15 + pnpm-lock.yaml | 15 +- 41 files changed, 874 insertions(+), 935 deletions(-) create mode 100644 .specs/live-viewer-rpc.md delete mode 100644 apps/cli/src/live-view-url.ts create mode 100644 apps/cli/src/live-viewer-server.ts create mode 100644 packages/browser/src/mcp/live-viewer-client.ts delete mode 100644 packages/recorder/src/live-view-server.ts delete mode 100644 packages/recorder/src/replay-broadcast.ts delete mode 100644 packages/recorder/src/replay-viewer.ts delete mode 100644 packages/recorder/src/viewer-events.ts delete mode 100644 packages/recorder/tests/live-view-server.test.ts delete mode 100644 packages/recorder/tests/replay-viewer.test.ts create mode 100644 packages/recorder/viewer/src/atoms/live-updates.ts delete mode 100644 packages/recorder/viewer/src/hooks/use-mount-effect.ts create mode 100644 packages/recorder/viewer/src/injected-events.ts create mode 100644 packages/recorder/viewer/src/rpc/client.ts create mode 100644 packages/shared/src/rpc/live-viewer.rpc.ts create mode 100644 packages/shared/src/rpcs.ts create mode 100644 packages/supervisor/src/live-viewer.ts create mode 100644 packages/supervisor/src/rpc/live-viewer.rpc.layer.ts diff --git a/.specs/live-viewer-rpc.md b/.specs/live-viewer-rpc.md new file mode 100644 index 00000000..abd57592 --- /dev/null +++ b/.specs/live-viewer-rpc.md @@ -0,0 +1,440 @@ +# Live Viewer RPC Refactor + +## Problem + +The MCP server process currently hosts a Vite dev server to serve the live viewer. This is wrong: + +- The MCP server is a subprocess two hops away from the CLI (CLI -> Agent ACP adapter -> MCP server) +- The MCP server has no business running a Vite dev server +- The viewer uses hacky custom middleware (`testie-serve` plugin) to serve `.expect/` files +- Config is passed via env vars because there's no direct communication channel +- The live viewer HTML is not truly standalone — it fetches `.ndjson` files at runtime + +## Architecture: Before + +``` +CLI process + └─ spawns Agent ACP adapter + └─ spawns Browser MCP server + ├─ controls Chromium (Playwright + rrweb) + ├─ polls rrweb events every 500ms + ├─ runs Vite dev server for live viewer ← wrong + └─ writes .expect/ files on close +``` + +## Architecture: After + +``` +CLI process + ├─ starts LiveViewer RPC server (WebSocket) ← new, owned by CLI + ├─ serves viewer React app (Vite) ← new, owned by CLI + ├─ receives ExecutionEvents from executor stream (already exists) + └─ spawns Agent ACP adapter + └─ spawns Browser MCP server + ├─ controls Chromium (Playwright + rrweb) + ├─ polls rrweb events every 500ms + └─ pushes rrweb events to CLI via RPC ← new +``` + +## Design + +### 1. RPC Definition — `packages/shared/src/rpc/live-viewer.rpc.ts` + +Following the ami-next pattern (`RpcGroup.make` + `.prefix`): + +```ts +import { Schema } from "effect"; +import { Rpc, RpcGroup } from "effect/unstable/rpc"; +import { ExecutionEvent } from "../models"; + +export const RrwebEvent = Schema.Unknown; +// rrweb's eventWithTime is complex and changes between versions. +// We treat it as opaque JSON — the viewer's rrweb-player deserializes it. + +export const LiveUpdatePayload = Schema.Union([ + Schema.TaggedStruct("RrwebBatch", { + events: Schema.Array(RrwebEvent), + }), + Schema.TaggedStruct("Execution", { + event: ExecutionEvent, + }), +]); +export type LiveUpdatePayload = typeof LiveUpdatePayload.Type; + +const LiveViewerRpcsBase = RpcGroup.make( + // MCP server calls this to push rrweb events to the CLI + Rpc.make("PushRrwebEvents", { + success: Schema.Void, + payload: { + events: Schema.Array(RrwebEvent), + }, + }), + + // Viewer connects to this to get a merged stream of all live updates + Rpc.make("StreamEvents", { + success: LiveUpdatePayload, + stream: true, + }), +); + +export const LiveViewerRpcs = LiveViewerRpcsBase.prefix("liveViewer."); +``` + +### 2. LiveViewer Service — `packages/supervisor/src/live-viewer.ts` + +The service owns the PubSub and provides the RPC handlers. Lives in `packages/supervisor` because it depends on execution state (ExecutionEvents) which the supervisor already owns. + +```ts +import { Effect, Layer, PubSub, Stream, ServiceMap } from "effect"; +import type { LiveUpdatePayload } from "@expect/shared/rpc/live-viewer.rpc"; +import type { ExecutionEvent } from "@expect/shared/models"; +import type { eventWithTime } from "@rrweb/types"; + +export class LiveViewer extends ServiceMap.Service()("@supervisor/LiveViewer", { + make: Effect.gen(function* () { + const pubsub = yield* PubSub.unbounded(); + + const push = Effect.fn("LiveViewer.push")( + function* (payload: LiveUpdatePayload) { + yield* PubSub.publish(pubsub, payload); + }, + ); + + const stream = Stream.fromPubSub(pubsub); + + return { push, stream } as const; + }), +}) { + static layer = Layer.effect(this)(this.make); +} +``` + +### 3. RPC Layer — `packages/supervisor/src/rpc/live-viewer.rpc.layer.ts` + +```ts +import { Effect, Layer } from "effect"; +import { LiveViewerRpcs } from "@expect/shared/rpc/live-viewer.rpc"; +import { LiveViewer } from "../live-viewer"; + +export const LiveViewerRpcsLive = LiveViewerRpcs.toLayer( + Effect.gen(function* () { + const liveViewer = yield* LiveViewer; + + return LiveViewerRpcs.of({ + "liveViewer.PushRrwebEvents": (req) => liveViewer.push({ _tag: "RrwebBatch", events: req.events }), + "liveViewer.StreamEvents": () => liveViewer.stream, + }); + }), +).pipe(Layer.provide(LiveViewer.layer)); +``` + +### 4. RPC Server (WebSocket) — started by CLI + +The CLI and MCP server agree on a fixed port for the LiveViewer RPC. Defined as a constant in `packages/shared` (e.g. `LIVE_VIEWER_RPC_PORT = 38930`). Both sides import it — no env var passing needed. + +```ts +// packages/shared/src/ports.ts +export const LIVE_VIEWER_RPC_PORT = 38930; +export const LIVE_VIEWER_RPC_URL = `ws://localhost:${LIVE_VIEWER_RPC_PORT}/rpc`; +``` + +CLI starts the server: + +```ts +const liveViewerServer = yield* RpcServer.layerHttp({ + group: LiveViewerRpcs, + path: "/rpc", +}).pipe(Layer.provide(LiveViewerRpcsLive)); +// served on LIVE_VIEWER_RPC_PORT +``` + +MCP server connects as a client using the same constant — no env vars, no URL passing. + +### 5. MCP Server Changes — `packages/browser/src/mcp/mcp-session.ts` + +Replace the broadcast + live view server with an RPC client call: + +```ts +// Instead of: +// const broadcast = yield* makeReplayBroadcast; +// yield* startLiveViewServer(liveViewUrl, broadcast); +// +// Do: +const rpcClient = yield* LiveViewerRpcClient; + +// In the polling fiber: +const pollPage = evaluateRecorderRuntime(page, "getEvents").pipe( + Effect.tap((events) => + events.length > 0 + ? rpcClient("liveViewer.PushRrwebEvents", { events }) + : Effect.void, + ), +); +``` + +The MCP server no longer owns any PubSub, broadcast, or Vite server. It just polls rrweb and pushes events upstream. + +### 6. Viewer React App + +#### RPC Client + Runtime — `packages/recorder/viewer/src/rpc/client.ts` + +```ts +import { Layer, Logger, References } from "effect"; +import { AtomRpc } from "effect/unstable/reactivity"; +import * as Atom from "effect/unstable/reactivity/Atom"; +import { RpcClient, RpcSerialization } from "effect/unstable/rpc"; +import { Socket } from "effect/unstable/socket"; + +import { LiveViewerRpcs } from "@expect/shared/rpc/live-viewer.rpc"; +import { LIVE_VIEWER_RPC_PORT } from "@expect/shared/ports"; + +const protocol = RpcClient.layerProtocolSocket().pipe( + Layer.provide( + Socket.layerWebSocket(`ws://localhost:${LIVE_VIEWER_RPC_PORT}/rpc`), + ), + Layer.provide(Socket.layerWebSocketConstructorGlobal), + Layer.provide(RpcSerialization.layerNdjson), +); + +export class ViewerClient extends AtomRpc.Service()("ViewerClient", { + group: LiveViewerRpcs, + protocol, +}) {} + +const ViewerLive = ViewerClient.layer.pipe( + Layer.provideMerge(Layer.succeed(References.MinimumLogLevel)("Error")), + Layer.provideMerge(Logger.layer([Logger.consolePretty()])), +); + +export const ViewerRuntime = Atom.runtime(ViewerLive); +``` + +#### Atoms — `packages/recorder/viewer/src/atoms/live-updates.ts` + +```ts +import { Effect, Stream } from "effect"; +import * as Atom from "effect/unstable/reactivity/Atom"; +import type { eventWithTime } from "@rrweb/types"; + +import { ViewerClient, ViewerRuntime } from "../rpc/client"; +import type { LiveUpdatePayload } from "@expect/shared/rpc/live-viewer.rpc"; +import type { ExecutionEvent } from "@expect/shared/models"; + +// Accumulated rrweb events for the player +export const rrwebEventsAtom = Atom.make([]); + +// Latest execution events for the steps panel +export const executionEventsAtom = Atom.make([]); + +// Stream consumer — subscribes on mount, pushes to atoms +export const liveUpdatesAtom = ViewerRuntime.pull( + (get: Atom.Context) => + Effect.gen(function* () { + const client = yield* ViewerClient; + return client("liveViewer.StreamEvents", {}).pipe( + Stream.tap((payload) => + Effect.sync(() => { + if (payload._tag === "RrwebBatch") { + Atom.unsafeSet( + rrwebEventsAtom, + [...Atom.unsafeGet(rrwebEventsAtom), ...payload.events] as readonly eventWithTime[], + ); + } else { + Atom.unsafeSet( + executionEventsAtom, + [...Atom.unsafeGet(executionEventsAtom), payload.event], + ); + } + }), + ), + ); + }), +); +``` + +#### App Component — `packages/recorder/viewer/src/app.tsx` + +```tsx +import { useAtomValue } from "effect/unstable/reactivity/Atom"; +import { RegistryProvider } from "@effect/atom-react"; +import { rrwebEventsAtom, executionEventsAtom, liveUpdatesAtom } from "./atoms/live-updates"; +import { ViewerRuntime } from "./rpc/client"; +import { RrwebPlayer } from "./components/rrweb-player"; +import { StepsPanel } from "./components/steps-panel"; + +const AppInner = () => { + const rrwebEvents = useAtomValue(rrwebEventsAtom); + const executionEvents = useAtomValue(executionEventsAtom); + + // Subscribe to the live update stream + useAtomValue(liveUpdatesAtom); + + return ( +
      + + +
      + ); +}; + +export const App = () => ( + + + +); +``` + +#### Entry Point — `packages/recorder/viewer/src/main.tsx` + +```tsx +import { createRoot } from "react-dom/client"; +import { App } from "./app"; +import "./style.css"; + +createRoot(document.getElementById("root")!).render(); +``` + +The viewer is a standalone React app. No custom middleware, no SSE, no file-serving plugins. It connects to the LiveViewer RPC via WebSocket and reactively updates atoms as events stream in. + +### 7. Viewer Serving + +The viewer React app is **built at package build time** via Vite (just as a bundler). At runtime, the CLI serves the pre-built static files using Effect's `HttpStaticServer` — no Vite dependency at runtime, no custom middleware. + +Build: the `packages/recorder/viewer/` app is built by Vite into a static directory (e.g. `packages/recorder/dist/viewer/`). + +Serving at runtime: + +```ts +import { HttpStaticServer } from "effect/unstable/http"; +import { NodeHttpServer, NodeServices } from "@effect/platform-node"; +import { HttpRouter } from "effect/unstable/http"; +import { createServer } from "node:http"; + +const VIEWER_STATIC_DIR = join(dirname(fileURLToPath(import.meta.url)), "viewer"); + +const StaticFilesLive = HttpStaticServer.layer({ + root: VIEWER_STATIC_DIR, + spa: true, // all routes fall back to index.html +}); + +const layerViewerServer = (port: number) => + StaticFilesLive.pipe( + Layer.provideMerge(HttpRouter.serve(StaticFilesLive)), + Layer.provide( + NodeHttpServer.layer(() => createServer(), { port }), + ), + Layer.provide(NodeServices.layer), + Layer.provide(HttpRouter.layer), + ); +``` + +This serves the viewer SPA on one port. The RPC WebSocket runs on a separate port (or the same server, TBD). The viewer app connects to the RPC endpoint via WebSocket to get the event stream. + +No Vite at runtime. No `testie-serve` middleware. No custom file-serving plugins. Just pre-built static files + a WebSocket RPC. + +### 8. Execution Events Integration + +The CLI already has `ExecutedTestPlan` updates via the executor stream (`Stream.tap` in `execution-atom.ts`). Wire the `onUpdate` callback to also push to `LiveViewer.pushExecutionEvent`: + +```ts +// In execution-atom.ts +const finalExecuted = yield* executor.executePlan(input.testPlan).pipe( + Stream.tap((executed) => + Effect.gen(function* () { + input.onUpdate(executed); + // Push latest event to live viewer + const latestEvent = executed.events.at(-1); + if (latestEvent) yield* liveViewer.push({ _tag: "Execution", event: latestEvent }); + }), + ), + Stream.runLast, + // ... +); +``` + +## What Gets Deleted + +- `packages/recorder/src/live-view-server.ts` — entire file +- `packages/recorder/src/replay-broadcast.ts` — entire file (LiveViewer service replaces it) +- `packages/recorder/viewer/vite.config.ts` — the `testie-serve` plugin +- `packages/browser/src/mcp/mcp-session.ts` — broadcast refs, live view refs, `startLiveViewServer` call, `flushBroadcastToFile` +- `EXPECT_LIVE_VIEW_URL_ENV_NAME` — no longer needed, replaced by shared constant +- All SSE-related code in the viewer app +- `packages/recorder/src/replay-viewer.ts` — `buildReplayViewerHtml` replaced by copying pre-built viewer + inlining data + +## What Stays + +- `packages/recorder/src/runtime/index.ts` — browser-side rrweb recording (unchanged) +- `packages/recorder/src/utils/evaluate-runtime.ts` — Playwright bridge (unchanged) +- `packages/recorder/viewer/src/` — React viewer app (refactored to use RPC WebSocket instead of SSE) +- rrweb event polling in MCP session (unchanged, but pushes to RPC instead of broadcast) +- Static report generation — reworked to copy pre-built viewer + inline data (see section 9) + +### 9. Static Reports + +`buildReplayViewerHtml` is deleted. Instead, static reports reuse the exact same viewer app. + +#### Viewer source code + +The viewer declares a global sentinel at the top level: + +```ts +// packages/recorder/viewer/src/injected-events.ts +export const INJECTED_EVENTS: readonly LiveUpdatePayload[] | undefined = + (globalThis as any).__EXPECT_INJECTED_EVENTS__; +``` + +#### The atom adapts with one line + +```ts +// In live-updates.ts +export const liveUpdatesAtom = ViewerRuntime.pull( + (get: Atom.Context) => + Effect.gen(function* () { + // Static report: events are inlined in the HTML, no WebSocket needed + if (INJECTED_EVENTS) { + return Stream.fromIterable(INJECTED_EVENTS); + } + + // Live mode: stream from RPC WebSocket + const client = yield* ViewerClient; + return client("liveViewer.StreamEvents", {}); + }), +); +``` + +Same atoms, same components, same rrweb-player, same steps panel. The only difference is where the stream comes from. + +#### Viewer source code (preventing dead-code elimination) + +The sentinel is defined directly in the bundle source so Vite doesn't optimize it away: + +```ts +// packages/recorder/viewer/src/injected-events.ts + +// @ts-ignore — replaced by string substitution when generating static reports +// biome-ignore lint: intentional unused-looking assignment +export const __EXPECT_INJECTED_EVENTS__: readonly LiveUpdatePayload[] | undefined = undefined; +``` + +#### Generating a static report + +1. Copy the pre-built viewer directory to the report output path +2. Read the JS bundle, do a simple string replace: + ```ts + const bundle = readFileSync(bundlePath, "utf-8"); + const patched = bundle.replace( + "__EXPECT_INJECTED_EVENTS__=void 0", // Vite minifies `undefined` to `void 0` + `__EXPECT_INJECTED_EVENTS__=${JSON.stringify(events)}`, + ); + writeFileSync(bundlePath, patched); + ``` +3. Done — open the HTML file, it works offline with no server + +No script tag injection, no HTML parsing. Just a string replace on the already-built bundle. + +## Resolved Questions + +1. **RPC transport**: `RpcServer.layerHttp` handles WebSocket upgrade automatically — WebSocket is the default protocol. No separate WebSocket layer needed. Same pattern as ami-next's `server.ts`. Use `RpcSerialization.layerNdjson` for message encoding. + +2. **Port allocation**: Fixed port constant shared between CLI and MCP server (`LIVE_VIEWER_RPC_PORT`). No env vars. diff --git a/apps/cli/src/components/screens/results-screen.tsx b/apps/cli/src/components/screens/results-screen.tsx index 8bcdbd1a..124d7a55 100644 --- a/apps/cli/src/components/screens/results-screen.tsx +++ b/apps/cli/src/components/screens/results-screen.tsx @@ -4,7 +4,7 @@ import { Option } from "effect"; import type { TestReport } from "@expect/supervisor"; import { copyToClipboard } from "../../utils/copy-to-clipboard.js"; import { openUrl } from "../../utils/open-url.js"; -import { LIVE_VIEW_URL } from "../../live-view-url.js"; +import { LIVE_VIEWER_STATIC_URL as LIVE_VIEW_URL } from "@expect/shared"; import { useColors } from "../theme-context.js"; import { RuledBox } from "../ui/ruled-box.js"; import { ScreenHeading } from "../ui/screen-heading.js"; diff --git a/apps/cli/src/components/screens/testing-screen.tsx b/apps/cli/src/components/screens/testing-screen.tsx index 2ea99975..95184cac 100644 --- a/apps/cli/src/components/screens/testing-screen.tsx +++ b/apps/cli/src/components/screens/testing-screen.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from "react"; import { Box, Static, Text, useInput } from "ink"; import figures from "figures"; -import { DateTime, Option } from "effect"; +import { Cause, DateTime, Option } from "effect"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import { useAtom, useAtomValue } from "@effect/atom-react"; import { @@ -24,7 +24,7 @@ import { usePlanStore } from "../../stores/use-plan-store.js"; import { usePlanExecutionStore } from "../../stores/use-plan-execution-store.js"; import { usePreferencesStore } from "../../stores/use-preferences.js"; import { useNavigationStore, Screen } from "../../stores/use-navigation.js"; -import { LIVE_VIEW_URL } from "../../live-view-url.js"; +import { LIVE_VIEWER_STATIC_URL as LIVE_VIEW_URL } from "@expect/shared"; import { openUrl } from "../../utils/open-url.js"; import { ScreenHeading } from "../ui/screen-heading.js"; import cliTruncate from "cli-truncate"; @@ -32,7 +32,10 @@ import { formatElapsedTime } from "../../utils/format-elapsed-time.js"; import { Image } from "../ui/image.js"; import { ErrorMessage } from "../ui/error-message.js"; import { createPlanFn } from "../../data/planning-atom.js"; -import { executePlanFn, screenshotPathsAtom } from "../../data/execution-atom.js"; +import { + executePlanFn, + screenshotPathsAtom, +} from "../../data/execution-atom.js"; interface TestingScreenProps { changesFor: ChangesFor; @@ -47,7 +50,10 @@ const getStepElapsedMs = (step: TestPlanStep): number | undefined => { return endMs - DateTime.toEpochMillis(step.startedAt.value); }; -export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) => { +export const TestingScreen = ({ + changesFor, + instruction, +}: TestingScreenProps) => { const setScreen = useNavigationStore((state) => state.setScreen); const COLORS = useColors(); @@ -62,22 +68,39 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = }); const screenshotPaths = useAtomValue(screenshotPathsAtom); - const testPlan = AsyncResult.isSuccess(planResult) ? planResult.value : undefined; + const testPlan = AsyncResult.isSuccess(planResult) + ? planResult.value + : undefined; const isPlanning = AsyncResult.isWaiting(planResult); - const isExecutingPlan = Boolean(testPlan) && AsyncResult.isWaiting(executionResult); + const isExecutingPlan = + Boolean(testPlan) && AsyncResult.isWaiting(executionResult); const isExecutionComplete = AsyncResult.isSuccess(executionResult); const report = isExecutionComplete ? executionResult.value.report : undefined; - const [executedPlan, setExecutedPlan] = useState(undefined); - const [runStartedAt, setRunStartedAt] = useState(undefined); + const [executedPlan, setExecutedPlan] = useState< + ExecutedTestPlan | undefined + >(undefined); + const [runStartedAt, setRunStartedAt] = useState( + undefined + ); const [elapsedTimeMs, setElapsedTimeMs] = useState(0); const [showCancelConfirmation, setShowCancelConfirmation] = useState(false); - const elapsedTimeLabel = useMemo(() => formatElapsedTime(elapsedTimeMs), [elapsedTimeMs]); + const elapsedTimeLabel = useMemo( + () => formatElapsedTime(elapsedTimeMs), + [elapsedTimeMs] + ); + + console.error("PLAN RESULT"); + if (planResult._tag === "Failure") { + console.error(Cause.pretty(planResult.cause)); + } + console.error(JSON.stringify(planResult)); useEffect(() => { setRunStartedAt(Date.now()); + console.error(`triggering create plan`); triggerCreatePlan({ changesFor, flowInstruction: instruction, @@ -146,7 +169,10 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = } if (key.escape) { - if (AsyncResult.isFailure(planResult) || AsyncResult.isFailure(executionResult)) { + if ( + AsyncResult.isFailure(planResult) || + AsyncResult.isFailure(executionResult) + ) { goToMain(); return; } @@ -166,21 +192,26 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = const planToRender = executedPlan ?? testPlan; const completedCount = planToRender?.steps - ? planToRender.steps.filter((step) => step.status === "passed" || step.status === "failed") - .length + ? planToRender.steps.filter( + (step) => step.status === "passed" || step.status === "failed" + ).length : 0; const totalCount = planToRender?.steps ? planToRender.steps.length : 0; - const currentActiveStep = planToRender?.steps?.find((step) => step.status === "active"); + const currentActiveStep = planToRender?.steps?.find( + (step) => step.status === "active" + ); const runStatusLabel = isPlanning ? "Planning" : currentActiveStep - ? `Running ${currentActiveStep.title}` - : completedCount === totalCount && totalCount > 0 - ? "Finishing up" - : "Starting"; + ? `Running ${currentActiveStep.title}` + : completedCount === totalCount && totalCount > 0 + ? "Finishing up" + : "Starting"; const filledWidth = - totalCount > 0 ? Math.round((completedCount / totalCount) * PROGRESS_BAR_WIDTH) : 0; + totalCount > 0 + ? Math.round((completedCount / totalCount) * PROGRESS_BAR_WIDTH) + : 0; const emptyWidth = PROGRESS_BAR_WIDTH - filledWidth; return ( @@ -207,31 +238,37 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = {` ${completedCount}/${totalCount}`} - {isExecutingPlan || isPlanning ? ` ${figures.pointerSmall} ${elapsedTimeLabel}` : ""} + {isExecutingPlan || isPlanning + ? ` ${figures.pointerSmall} ${elapsedTimeLabel}` + : ""}
      {(planToRender?.steps ?? []).map((step, stepIndex) => { const stepPrefix = `Step ${stepIndex + 1}`; - const label = Option.isSome(step.summary) ? step.summary.value : step.title; + const label = Option.isSome(step.summary) + ? step.summary.value + : step.title; const stepElapsedMs = getStepElapsedMs(step); const stepElapsedLabel = - stepElapsedMs !== undefined ? formatElapsedTime(stepElapsedMs) : undefined; + stepElapsedMs !== undefined + ? formatElapsedTime(stepElapsedMs) + : undefined; return ( {step.status === "passed" ? ( {` ${figures.tick} ${stepPrefix} ${cliTruncate( label, - TESTING_TOOL_TEXT_CHAR_LIMIT, + TESTING_TOOL_TEXT_CHAR_LIMIT )}${stepElapsedLabel ? ` ${stepElapsedLabel}` : ""}`} ) : step.status === "failed" ? ( {` ${figures.cross} ${stepPrefix} ${cliTruncate( label, - TESTING_TOOL_TEXT_CHAR_LIMIT, + TESTING_TOOL_TEXT_CHAR_LIMIT )}${stepElapsedLabel ? ` ${stepElapsedLabel}` : ""}`} ) : step.status === "active" ? ( @@ -241,14 +278,16 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = ) : ( - {` ○ ${stepPrefix} ${step.title}`} + {` ○ ${stepPrefix} ${step.title}`} )} ); @@ -272,12 +311,14 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = Stop this browser run? - This will terminate the agent and close the browser. + + This will terminate the agent and close the browser. + Press Enter or{" "} y to stop, or{" "} - Esc or n to - keep it running. + Esc or{" "} + n to keep it running. ) : null} @@ -290,7 +331,8 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = highlightColor={COLORS.PRIMARY} /> - Press o to open live view in browser. + Press o to open live view in + browser.
      ) : null} @@ -308,14 +350,23 @@ export const TestingScreen = ({ changesFor, instruction }: TestingScreenProps) = {AsyncResult.builder(planResult) .onError((error) => ( - + + + )) + .onDefect((defect) => ( + + )) .orNull()} {AsyncResult.builder(executionResult) .onError((error) => ( - + )) .orNull()} diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index 34c4e01d..7ddf3bdb 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -29,9 +29,6 @@ export const ALT_SCREEN_ON = "\u001b[?1049h\u001b[2J\u001b[H"; export const ALT_SCREEN_OFF = "\u001b[?1049l"; export const FALLBACK_TERMINAL_COLUMNS = 80; export const FALLBACK_TERMINAL_ROWS = 24; -export const LIVE_VIEW_PORT_RANGE_START = 17400; -export const LIVE_VIEW_PORT_RANGE_SIZE = 600; -export const LIVE_VIEW_READY_POLL_INTERVAL_MS = 1000; export const CLICK_SUPPORT_ENABLED = process.env.SUPPORT_CLICK === "true" || process.env.SUPPORT_CLICK === "1"; diff --git a/apps/cli/src/index.tsx b/apps/cli/src/index.tsx index 29da5fd8..c5b84772 100644 --- a/apps/cli/src/index.tsx +++ b/apps/cli/src/index.tsx @@ -1,4 +1,3 @@ -import { join } from "node:path"; import { Effect, Option } from "effect"; import { Command } from "commander"; import { render } from "ink"; @@ -7,14 +6,7 @@ import { App } from "./components/app.js"; import { ALT_SCREEN_OFF, ALT_SCREEN_ON, VERSION } from "./constants.js"; import { ThemeProvider } from "./components/theme-context.js"; import { loadThemeName } from "./utils/load-theme.js"; -import { - ChangesFor, - Git, - TestPlanDraft, - DraftId, - EXPECT_STATE_DIR, - REPLAY_FILE_NAME, -} from "@expect/supervisor"; +import { ChangesFor, Git, TestPlanDraft, DraftId } from "@expect/supervisor"; import { runHeadless } from "./utils/run-test.js"; import type { AgentBackend } from "@expect/agent"; import { useNavigationStore, Screen } from "./stores/use-navigation.js"; @@ -24,16 +16,6 @@ import { queryClient } from "./query-client.js"; import { setInkInstance } from "./utils/clear-ink-display.js"; import { RegistryProvider } from "@effect/atom-react"; import { agentProviderAtom } from "./data/runtime.js"; -import { LIVE_VIEW_URL } from "./live-view-url.js"; -import { EXPECT_LIVE_VIEW_URL_ENV_NAME } from "@expect/browser/mcp"; - -process.env[EXPECT_LIVE_VIEW_URL_ENV_NAME] = LIVE_VIEW_URL; - -process.env.BROWSER_TESTER_REPLAY_OUTPUT_PATH = join( - process.cwd(), - EXPECT_STATE_DIR, - REPLAY_FILE_NAME, -); const DEFAULT_SKIP_PLANNING = true; diff --git a/apps/cli/src/layers.ts b/apps/cli/src/layers.ts index 5ef8f222..a332e76d 100644 --- a/apps/cli/src/layers.ts +++ b/apps/cli/src/layers.ts @@ -1,7 +1,8 @@ import { Cause, Layer, Logger, References } from "effect"; import { DevTools } from "effect/unstable/devtools"; -import { Executor, Git, Planner, Reporter, Updates } from "@expect/supervisor"; +import { Executor, Git, LiveViewer, Planner, Reporter, Updates } from "@expect/supervisor"; import { Agent, AgentBackend } from "@expect/agent"; +import { layerLiveViewerRpcServer, layerLiveViewerStaticServer } from "./live-viewer-server.js"; const stderrLogger = Logger.make(({ logLevel, message, date, cause }) => { console.error( @@ -19,8 +20,11 @@ export const layerCli = ({ verbose, agent }: { verbose: boolean; agent: AgentBac Executor.layer, Reporter.layer, Updates.layer, + LiveViewer.layer, DevTools.layer(), Git.withRepoRoot(process.cwd()), + layerLiveViewerRpcServer, + layerLiveViewerStaticServer, ).pipe( Layer.provide(Agent.layerFor(agent ?? "claude")), Layer.provide(Logger.layer([stderrLogger])), diff --git a/apps/cli/src/live-view-url.ts b/apps/cli/src/live-view-url.ts deleted file mode 100644 index 2fe816da..00000000 --- a/apps/cli/src/live-view-url.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { LIVE_VIEW_PORT_RANGE_START, LIVE_VIEW_PORT_RANGE_SIZE } from "./constants.js"; - -const liveViewPort = - LIVE_VIEW_PORT_RANGE_START + Math.floor(Math.random() * LIVE_VIEW_PORT_RANGE_SIZE); - -export const LIVE_VIEW_URL = `http://127.0.0.1:${liveViewPort}`; diff --git a/apps/cli/src/live-viewer-server.ts b/apps/cli/src/live-viewer-server.ts new file mode 100644 index 00000000..56f6e247 --- /dev/null +++ b/apps/cli/src/live-viewer-server.ts @@ -0,0 +1,41 @@ +import { createServer } from "node:http"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { NodeHttpServer, NodeServices } from "@effect/platform-node"; +import { Layer } from "effect"; +import { HttpRouter, HttpStaticServer } from "effect/unstable/http"; +import { RpcSerialization, RpcServer } from "effect/unstable/rpc"; +import { LiveViewerRpcs } from "@expect/shared/rpcs"; +import { LIVE_VIEWER_RPC_PORT, LIVE_VIEWER_STATIC_PORT } from "@expect/shared"; +import { LiveViewerRpcsLive, LiveViewer } from "@expect/supervisor"; + +const VIEWER_STATIC_DIR = join( + dirname(fileURLToPath(import.meta.resolve("@expect/recorder"))), + "viewer", +); + +const RpcLive = RpcServer.layerHttp({ + group: LiveViewerRpcs, + path: "/rpc", +}).pipe(Layer.provide(LiveViewerRpcsLive)); + +export const layerLiveViewerRpcServer = RpcLive.pipe( + Layer.provideMerge(HttpRouter.serve(RpcLive, { disableListenLog: true })), + Layer.provide(NodeHttpServer.layer(() => createServer(), { port: LIVE_VIEWER_RPC_PORT })), + Layer.provide(RpcSerialization.layerNdjson), + Layer.provide(NodeServices.layer), + Layer.provide(HttpRouter.layer), + Layer.provide(LiveViewer.layer), +); + +const StaticFilesLive = HttpStaticServer.layer({ + root: VIEWER_STATIC_DIR, + spa: true, +}); + +export const layerLiveViewerStaticServer = StaticFilesLive.pipe( + Layer.provideMerge(HttpRouter.serve(StaticFilesLive, { disableListenLog: true })), + Layer.provide(NodeHttpServer.layer(() => createServer(), { port: LIVE_VIEWER_STATIC_PORT })), + Layer.provide(NodeServices.layer), + Layer.provide(HttpRouter.layer), +); diff --git a/packages/browser/package.json b/packages/browser/package.json index 538dfe4f..82067ef7 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -41,6 +41,7 @@ "@effect/platform-node": "4.0.0-beta.28", "@expect/cookies": "workspace:*", "@expect/recorder": "workspace:*", + "@expect/shared": "workspace:*", "@medv/finder": "^4.0.2", "@modelcontextprotocol/sdk": "^1.27.1", "effect": "4.0.0-beta.28", diff --git a/packages/browser/src/mcp/constants.ts b/packages/browser/src/mcp/constants.ts index 84371556..27e5fd92 100644 --- a/packages/browser/src/mcp/constants.ts +++ b/packages/browser/src/mcp/constants.ts @@ -1,3 +1 @@ -export const EXPECT_LIVE_VIEW_URL_ENV_NAME = "EXPECT_LIVE_VIEW_URL"; -export const EXPECT_REPLAY_OUTPUT_ENV_NAME = "EXPECT_REPLAY_OUTPUT_PATH"; export const LIVE_VIEW_PAGE_POLL_INTERVAL_MS = 500; diff --git a/packages/browser/src/mcp/index.ts b/packages/browser/src/mcp/index.ts index 56858692..2558f88f 100644 --- a/packages/browser/src/mcp/index.ts +++ b/packages/browser/src/mcp/index.ts @@ -1,4 +1,3 @@ -export { EXPECT_LIVE_VIEW_URL_ENV_NAME, EXPECT_REPLAY_OUTPUT_ENV_NAME } from "./constants"; export { McpSession } from "./mcp-session"; export { McpRuntime } from "./runtime"; export { createBrowserMcpServer, startBrowserMcpServer } from "./server"; diff --git a/packages/browser/src/mcp/live-viewer-client.ts b/packages/browser/src/mcp/live-viewer-client.ts new file mode 100644 index 00000000..c82e52fe --- /dev/null +++ b/packages/browser/src/mcp/live-viewer-client.ts @@ -0,0 +1,23 @@ +import { Layer, ServiceMap } from "effect"; +import { NodeSocket } from "@effect/platform-node"; +import * as RpcClient from "effect/unstable/rpc/RpcClient"; +import type { RpcClientError } from "effect/unstable/rpc/RpcClientError"; +import type * as RpcGroup from "effect/unstable/rpc/RpcGroup"; +import { RpcSerialization } from "effect/unstable/rpc"; +import { LiveViewerRpcs } from "@expect/shared/rpcs"; +import { LIVE_VIEWER_RPC_URL } from "@expect/shared"; + +type Client = RpcClient.RpcClient, RpcClientError>; + +const protocolLayer = RpcClient.layerProtocolSocket().pipe( + Layer.provide(NodeSocket.layerWebSocket(LIVE_VIEWER_RPC_URL)), + Layer.provide(RpcSerialization.layerNdjson), +); + +export class LiveViewerClient extends ServiceMap.Service()( + "@browser/LiveViewerClient", +) { + static layer = Layer.effect(LiveViewerClient)(RpcClient.make(LiveViewerRpcs)).pipe( + Layer.provide(protocolLayer), + ); +} diff --git a/packages/browser/src/mcp/mcp-session.ts b/packages/browser/src/mcp/mcp-session.ts index ba373cb8..91be403b 100644 --- a/packages/browser/src/mcp/mcp-session.ts +++ b/packages/browser/src/mcp/mcp-session.ts @@ -1,24 +1,11 @@ -import { basename, dirname, extname, join } from "node:path"; -import { mkdirSync, writeFileSync } from "node:fs"; import type { Browser as PlaywrightBrowser, BrowserContext, Page } from "playwright"; -import { Config, Effect, Fiber, Layer, Option, Ref, Schedule, ServiceMap } from "effect"; -import { - collectAllEvents, - evaluateRecorderRuntime, - buildReplayViewerHtml, - makeReplayBroadcast, - startLiveViewServer, - EVENT_COLLECT_INTERVAL_MS, - type eventWithTime, - type LiveViewHandle, - type ReplayBroadcast, - type ViewerRunState, -} from "@expect/recorder"; +import { Effect, Fiber, Layer, Ref, Schedule, ServiceMap } from "effect"; +import { evaluateRecorderRuntime, EVENT_COLLECT_INTERVAL_MS } from "@expect/recorder"; import { Browser } from "../browser"; import { NavigationError } from "../errors"; import type { AnnotatedScreenshotOptions, SnapshotOptions, SnapshotResult } from "../types"; -import { EXPECT_LIVE_VIEW_URL_ENV_NAME, EXPECT_REPLAY_OUTPUT_ENV_NAME } from "./constants"; import { McpSessionNotOpenError } from "./errors"; +import { LiveViewerClient } from "./live-viewer-client"; interface ConsoleEntry { readonly type: string; @@ -40,8 +27,6 @@ export interface BrowserSessionData { readonly page: Page; readonly consoleMessages: ConsoleEntry[]; readonly networkRequests: NetworkEntry[]; - readonly replayOutputPath: string | undefined; - readonly broadcast: ReplayBroadcast; readonly trackedPages: Set; lastSnapshot: SnapshotResult | undefined; } @@ -56,11 +41,6 @@ export interface OpenResult { readonly injectedCookieCount: number; } -export interface CloseResult { - readonly replaySessionPath: string | undefined; - readonly reportPath: string | undefined; -} - const setupPageTracking = (page: Page, sessionData: BrowserSessionData) => { if (sessionData.trackedPages.has(page)) return; sessionData.trackedPages.add(page); @@ -91,56 +71,13 @@ const setupPageTracking = (page: Page, sessionData: BrowserSessionData) => { }); }; -const flushBroadcastToFile = (broadcast: ReplayBroadcast, outputPath: string) => - Effect.gen(function* () { - const allEvents = yield* broadcast.snapshotEvents; - if (allEvents.length === 0) return; - - const ndjson = allEvents.map((event) => JSON.stringify(event)).join("\n") + "\n"; - yield* Effect.try(() => { - mkdirSync(dirname(outputPath), { recursive: true }); - writeFileSync(outputPath, ndjson); - }); - - const runState = yield* broadcast.snapshotRunState; - const replayFileName = basename(outputPath); - const replayBaseName = basename(outputPath, extname(outputPath)); - const htmlReportPath = join(dirname(outputPath), `${replayBaseName}.html`); - const reportHtml = buildReplayViewerHtml({ - title: runState ? `Test Report: ${runState.title}` : "Expect Report", - eventsSource: { ndjsonPath: replayFileName }, - steps: runState, - }); - yield* Effect.try(() => writeFileSync(htmlReportPath, reportHtml)); - - if (runState) { - const runStateFilePath = join(dirname(outputPath), "run-state.json"); - yield* Effect.try(() => writeFileSync(runStateFilePath, JSON.stringify(runState))); - } - }); - export class McpSession extends ServiceMap.Service()("@browser/McpSession", { make: Effect.gen(function* () { const browserService = yield* Browser; - const replayOutputPath = yield* Config.option(Config.string(EXPECT_REPLAY_OUTPUT_ENV_NAME)); - const liveViewUrl = yield* Config.option(Config.string(EXPECT_LIVE_VIEW_URL_ENV_NAME)); + const rpcClient = yield* LiveViewerClient; const sessionRef = yield* Ref.make(undefined); - const liveViewRef = yield* Ref.make(undefined); const pollingFiberRef = yield* Ref.make | undefined>(undefined); - const broadcastRef = yield* Ref.make(undefined); - const outputPathRef = yield* Ref.make(undefined); - - yield* Effect.addFinalizer(() => - Effect.gen(function* () { - const broadcast = yield* Ref.get(broadcastRef); - const outputPath = yield* Ref.get(outputPathRef); - if (!broadcast || !outputPath) return; - yield* flushBroadcastToFile(broadcast, outputPath).pipe( - Effect.catchCause((cause) => Effect.logDebug("Finalizer flush failed", { cause })), - ); - }), - ); const requireSession = Effect.fn("McpSession.requireSession")(function* () { const session = yield* Ref.get(sessionRef); @@ -154,11 +91,16 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe const navigate = Effect.fn("McpSession.navigate")(function* ( url: string, - options: { waitUntil?: "load" | "domcontentloaded" | "networkidle" | "commit" } = {}, + options: { + waitUntil?: "load" | "domcontentloaded" | "networkidle" | "commit"; + } = {}, ) { const sessionData = yield* requireSession(); yield* Effect.tryPromise({ - try: () => sessionData.page.goto(url, { waitUntil: options.waitUntil ?? "load" }), + try: () => + sessionData.page.goto(url, { + waitUntil: options.waitUntil ?? "load", + }), catch: (cause) => new NavigationError({ url, @@ -167,13 +109,6 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe }); }); - const pushStepEvent = Effect.fn("McpSession.pushStepEvent")(function* (state: ViewerRunState) { - const broadcast = yield* Ref.get(broadcastRef); - if (broadcast) { - yield* broadcast.publishRunState(state); - } - }); - const open = Effect.fn("McpSession.open")(function* (url: string, options: OpenOptions = {}) { yield* Effect.annotateCurrentSpan({ url }); @@ -183,19 +118,12 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe waitUntil: options.waitUntil, }); - const broadcast = yield* makeReplayBroadcast; - yield* Ref.set(broadcastRef, broadcast); - - const outputPath = Option.getOrUndefined(replayOutputPath); - yield* Ref.set(outputPathRef, outputPath); const sessionData: BrowserSessionData = { browser: pageResult.browser, context: pageResult.context, page: pageResult.page, consoleMessages: [], networkRequests: [], - replayOutputPath: outputPath, - broadcast, trackedPages: new Set(), lastSnapshot: undefined, }; @@ -212,7 +140,7 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe return evaluateRecorderRuntime(page, "getEvents").pipe( Effect.tap((events) => { if (Array.isArray(events) && events.length > 0) { - return broadcast.publishEvents(events); + return rpcClient["liveViewer.PushRrwebEvents"]({ events }); } return Effect.void; }), @@ -229,20 +157,6 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe ); yield* Ref.set(pollingFiberRef, fiber); - const existingLiveView = yield* Ref.get(liveViewRef); - if (Option.isSome(liveViewUrl) && !existingLiveView) { - const handle = yield* startLiveViewServer(liveViewUrl.value, broadcast).pipe( - Effect.catchCause((cause) => - Effect.logDebug("Live view server failed to start", { cause }).pipe( - Effect.as(undefined), - ), - ), - ); - if (handle) { - yield* Ref.set(liveViewRef, handle); - } - } - const injectedCookieCount = yield* Effect.tryPromise(() => pageResult.context.cookies()).pipe( Effect.map((cookies) => cookies.length), Effect.catchCause((cause) => @@ -276,7 +190,7 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe const close = Effect.fn("McpSession.close")(function* () { const activeSession = yield* Ref.get(sessionRef); - if (!activeSession) return undefined; + if (!activeSession) return; yield* Ref.set(sessionRef, undefined); @@ -286,43 +200,9 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe yield* Ref.set(pollingFiberRef, undefined); } - if (!activeSession.page.isClosed()) { - const finalEvents = yield* collectAllEvents(activeSession.page).pipe( - Effect.catchCause((cause) => - Effect.logDebug("Failed to collect final replay events", { cause }).pipe( - Effect.as([] as ReadonlyArray), - ), - ), - ); - if (finalEvents.length > 0) { - yield* activeSession.broadcast.publishEvents(finalEvents); - } - } - - const liveView = yield* Ref.get(liveViewRef); - if (liveView) { - yield* liveView.close.pipe( - Effect.catchCause((cause) => Effect.logDebug("Failed to close live view", { cause })), - ); - yield* Ref.set(liveViewRef, undefined); - } - yield* Effect.tryPromise(() => activeSession.browser.close()).pipe( Effect.catchCause((cause) => Effect.logDebug("Failed to close browser", { cause })), ); - - const resolvedOutputPath = activeSession.replayOutputPath; - let replaySessionPath: string | undefined; - let reportPath: string | undefined; - if (resolvedOutputPath) { - replaySessionPath = resolvedOutputPath; - reportPath = join( - dirname(resolvedOutputPath), - `${basename(resolvedOutputPath, extname(resolvedOutputPath))}.html`, - ); - } - - return { replaySessionPath, reportPath } satisfies CloseResult; }); return { @@ -334,10 +214,12 @@ export class McpSession extends ServiceMap.Service()("@browser/McpSe snapshot, annotatedScreenshot, updateLastSnapshot, - pushStepEvent, close, } as const; }), }) { - static layer = Layer.effect(this)(this.make).pipe(Layer.provide(Browser.layer)); + static layer = Layer.effect(this)(this.make).pipe( + Layer.provide(Browser.layer), + Layer.provide(LiveViewerClient.layer), + ); } diff --git a/packages/browser/src/mcp/server.ts b/packages/browser/src/mcp/server.ts index aae945c6..2dd74274 100644 --- a/packages/browser/src/mcp/server.ts +++ b/packages/browser/src/mcp/server.ts @@ -258,8 +258,8 @@ export const createBrowserMcpServer = ( runMcp( Effect.gen(function* () { const session = yield* McpSession; - const result = yield* session.close(); - if (!result) return textResult("No browser open."); + if (!session.hasSession()) return textResult("No browser open."); + yield* session.close(); return textResult("Browser closed."); }), ), diff --git a/packages/recorder/package.json b/packages/recorder/package.json index 7d831bfb..1a19fa6b 100644 --- a/packages/recorder/package.json +++ b/packages/recorder/package.json @@ -20,7 +20,8 @@ } }, "scripts": { - "build": "vp pack", + "build:viewer": "vp build --config viewer/vite.config.ts", + "build": "vp pack && pnpm build:viewer", "dev": "vp pack --watch", "lint": "vp lint && tsc --noEmit", "format": "vp fmt", @@ -31,13 +32,14 @@ "typecheck": "tsgo --noEmit" }, "dependencies": { + "@effect/atom-react": "4.0.0-beta.35", "@effect/platform-node": "4.0.0-beta.28", + "@expect/shared": "workspace:*", "@rrweb/types": "^2.0.0-alpha.18", "effect": "4.0.0-beta.28", "playwright": "^1.52.0", "rrweb": "^2.0.0-alpha.18", - "rrweb-player": "^2.0.0-alpha.18", - "vite": "^7.3.1" + "rrweb-player": "^2.0.0-alpha.18" }, "devDependencies": { "@tailwindcss/vite": "^4.2.2", @@ -54,6 +56,7 @@ "tailwind-merge": "^3.5.0", "tailwindcss": "^4.2.1", "tw-animate-css": "^1.4.0", - "typescript": "^5.7.0" + "typescript": "^5.7.0", + "vite": "^7.3.1" } } diff --git a/packages/recorder/src/constants.ts b/packages/recorder/src/constants.ts index f1518f87..4e665140 100644 --- a/packages/recorder/src/constants.ts +++ b/packages/recorder/src/constants.ts @@ -2,7 +2,3 @@ export const EVENT_COLLECT_INTERVAL_MS = 500; export const REPLAY_PLAYER_WIDTH_PX = 960; export const REPLAY_PLAYER_HEIGHT_PX = 540; - -export const RUN_STATE_FILE_NAME = "run-state.json"; -export const EXPECT_STATE_DIR = ".expect"; -export const REPLAY_FILE_NAME = "browser-flow.ndjson"; diff --git a/packages/recorder/src/index.ts b/packages/recorder/src/index.ts index 64d7d10a..9ea97fc9 100644 --- a/packages/recorder/src/index.ts +++ b/packages/recorder/src/index.ts @@ -1,19 +1,10 @@ export { collectEvents, collectAllEvents, loadSession } from "./recorder"; -export { buildReplayViewerHtml } from "./replay-viewer"; -export { startLiveViewServer } from "./live-view-server"; -export type { LiveViewHandle } from "./live-view-server"; -export { makeReplayBroadcast } from "./replay-broadcast"; -export type { ReplayBroadcast } from "./replay-broadcast"; export { evaluateRecorderRuntime } from "./utils/evaluate-runtime"; export { RecorderInjectionError, SessionLoadError } from "./errors"; export type { CollectResult } from "./types"; -export type { ViewerRunState, ViewerStepEvent } from "./viewer-events"; export { EVENT_COLLECT_INTERVAL_MS, - REPLAY_FILE_NAME, REPLAY_PLAYER_WIDTH_PX, REPLAY_PLAYER_HEIGHT_PX, - RUN_STATE_FILE_NAME, - EXPECT_STATE_DIR, } from "./constants"; export type { eventWithTime } from "@rrweb/types"; diff --git a/packages/recorder/src/live-view-server.ts b/packages/recorder/src/live-view-server.ts deleted file mode 100644 index 4c6d050d..00000000 --- a/packages/recorder/src/live-view-server.ts +++ /dev/null @@ -1,149 +0,0 @@ -import type { ServerResponse } from "node:http"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; -import { Effect, Fiber, PubSub, Queue, Ref } from "effect"; -import type { ReplayBroadcast } from "./replay-broadcast"; - -const VIEWER_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../viewer"); - -export interface LiveViewHandle { - readonly url: string; - readonly close: Effect.Effect; -} - -const drainToSse = (broadcast: ReplayBroadcast, response: ServerResponse) => - Effect.scoped( - Effect.gen(function* () { - const snapshot = yield* broadcast.snapshotEvents; - if (snapshot.length > 0) { - yield* Effect.try(() => - response.write(`event: replay\ndata: ${JSON.stringify(snapshot)}\n\n`), - ); - } - - const runState = yield* broadcast.snapshotRunState; - if (runState) { - yield* Effect.try(() => - response.write(`event: steps\ndata: ${JSON.stringify(runState)}\n\n`), - ); - } - - const eventsQueue = yield* PubSub.subscribe(broadcast.eventsPubSub); - const stepsQueue = yield* PubSub.subscribe(broadcast.runStatePubSub); - - const writeSse = (event: string, data: string) => - Effect.try(() => { - response.write(`event: ${event}\ndata: ${data}\n\n`); - }); - - const forwardEvents = Effect.forever( - Effect.gen(function* () { - const batch = yield* Queue.take(eventsQueue); - yield* writeSse("replay", JSON.stringify(batch)); - }), - ); - - const forwardSteps = Effect.forever( - Effect.gen(function* () { - const state = yield* Queue.take(stepsQueue); - yield* writeSse("steps", JSON.stringify(state)); - }), - ); - - yield* Effect.all([forwardEvents, forwardSteps], { concurrency: "unbounded" }); - }), - ); - -export const startLiveViewServer = Effect.fn("LiveViewServer.start")(function* ( - liveViewUrl: string, - broadcast: ReplayBroadcast, -) { - const parsedUrl = new URL(liveViewUrl); - const activeFibers = yield* Ref.make>>(new Map()); - - const { createServer: createViteServer } = yield* Effect.tryPromise({ - try: () => import("vite"), - catch: (cause) => new Error(`Failed to load vite: ${cause}`), - }); - - const viteServer = yield* Effect.tryPromise({ - try: () => - createViteServer({ - root: VIEWER_ROOT, - server: { - host: parsedUrl.hostname, - port: Number(parsedUrl.port), - strictPort: true, - }, - logLevel: "silent", - plugins: [ - { - name: "expect-sse", - configureServer(server) { - server.middlewares.use((request, response, next) => { - if (request.url === "/events") { - response.writeHead(200, { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - Connection: "keep-alive", - }); - - const fiber = Effect.runFork(drainToSse(broadcast, response)); - Ref.getUnsafe(activeFibers).set(response, fiber); - - request.on("close", () => { - Ref.getUnsafe(activeFibers).delete(response); - Effect.runFork(Fiber.interrupt(fiber)); - }); - return; - } - - if (request.url === "/latest.json") { - Effect.runPromise(broadcast.snapshotEvents).then((events) => { - response.writeHead(200, { "Content-Type": "application/json" }); - response.end(JSON.stringify(events)); - }); - return; - } - - if (request.url === "/run-state.json") { - Effect.runPromise(broadcast.snapshotRunState).then((runState) => { - if (runState) { - response.writeHead(200, { "Content-Type": "application/json" }); - response.end(JSON.stringify(runState)); - } else { - response.writeHead(204); - response.end(); - } - }); - return; - } - - next(); - }); - }, - }, - ], - }), - catch: (cause) => new Error(`Failed to create Vite server: ${cause}`), - }); - - yield* Effect.tryPromise({ - try: () => viteServer.listen(), - catch: (cause) => new Error(`Failed to start Vite server: ${cause}`), - }); - - return { - url: parsedUrl.toString(), - close: Effect.gen(function* () { - const fibers = yield* Ref.get(activeFibers); - yield* Effect.forEach([...fibers.values()], (fiber) => Fiber.interrupt(fiber), { - concurrency: "unbounded", - }); - yield* Effect.tryPromise({ - try: () => viteServer.close(), - catch: () => new Error("Failed to close Vite server"), - }); - }), - } satisfies LiveViewHandle; -}); diff --git a/packages/recorder/src/replay-broadcast.ts b/packages/recorder/src/replay-broadcast.ts deleted file mode 100644 index 5cc92b2e..00000000 --- a/packages/recorder/src/replay-broadcast.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Effect, PubSub, Ref } from "effect"; -import type { eventWithTime } from "@rrweb/types"; -import type { ViewerRunState } from "./viewer-events"; - -export interface ReplayBroadcast { - readonly publishEvents: (events: readonly eventWithTime[]) => Effect.Effect; - readonly publishRunState: (state: ViewerRunState) => Effect.Effect; - readonly snapshotEvents: Effect.Effect; - readonly snapshotRunState: Effect.Effect; - readonly eventsPubSub: PubSub.PubSub; - readonly runStatePubSub: PubSub.PubSub; -} - -export const makeReplayBroadcast = Effect.gen(function* () { - const eventsRef = yield* Ref.make([]); - const runStateRef = yield* Ref.make(undefined); - const eventsPubSub = yield* PubSub.unbounded(); - const runStatePubSub = yield* PubSub.unbounded(); - - const publishEvents = (events: readonly eventWithTime[]) => - Effect.gen(function* () { - yield* Ref.update(eventsRef, (previous) => { - for (const event of events) previous.push(event); - return previous; - }); - yield* PubSub.publish(eventsPubSub, events); - }); - - const publishRunState = (state: ViewerRunState) => - Effect.gen(function* () { - yield* Ref.set(runStateRef, state); - yield* PubSub.publish(runStatePubSub, state); - }); - - return { - publishEvents, - publishRunState, - snapshotEvents: Ref.get(eventsRef), - snapshotRunState: Ref.get(runStateRef), - eventsPubSub, - runStatePubSub, - } satisfies ReplayBroadcast; -}); diff --git a/packages/recorder/src/replay-viewer.ts b/packages/recorder/src/replay-viewer.ts deleted file mode 100644 index 58f7ad7b..00000000 --- a/packages/recorder/src/replay-viewer.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { REPLAY_PLAYER_HEIGHT_PX, REPLAY_PLAYER_WIDTH_PX } from "./constants"; -import type { ViewerRunState } from "./viewer-events"; - -const escapeHtml = (text: string): string => - text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); - -const RRWEB_PLAYER_VERSION = "2.0.0-alpha.18"; -const RRWEB_PLAYER_CSS = `https://cdn.jsdelivr.net/npm/rrweb-player@${RRWEB_PLAYER_VERSION}/dist/style.css`; -const RRWEB_PLAYER_JS = `https://cdn.jsdelivr.net/npm/rrweb-player@${RRWEB_PLAYER_VERSION}/dist/rrweb-player.js`; - -type EventsSource = "sse" | { ndjsonPath: string }; - -interface ReplayViewerOptions { - title: string; - bodyHtml?: string; - eventsSource?: EventsSource; - steps?: ViewerRunState; -} - -const buildLoadEventsScript = (source: EventsSource): string => { - if (source === "sse") { - return ` - const res = await fetch('/latest.json'); - if (res.ok) { allEvents = await res.json(); if (allEvents.length >= 2) initPlayer(allEvents); } - const es = new EventSource('/events'); - es.addEventListener('replay', (msg) => { try { for (const e of JSON.parse(msg.data)) { allEvents.push(e); if (player) player.getReplayer().addEvent(e); } if (!player && allEvents.length >= 2) initPlayer(allEvents); } catch {} }); - es.addEventListener('steps', (msg) => { try { updateSteps(JSON.parse(msg.data)); } catch {} }); - es.onerror = () => { if (statusEl) statusEl.textContent = 'Connection lost. Retrying...'; };`; - } - - const escapedPath = source.ndjsonPath.replaceAll("'", "\\'"); - return ` - const res = await fetch('${escapedPath}'); - if (res.ok) { - allEvents = (await res.text()).trim().split('\\n').map(l => JSON.parse(l)); - if (allEvents.length >= 2) initPlayer(allEvents); - else if (statusEl) statusEl.textContent = 'No replay events recorded.'; - } else if (statusEl) statusEl.textContent = 'Failed to load replay.';`; -}; - -const buildStepsScript = (steps?: ViewerRunState): string => { - const initialData = steps ? JSON.stringify(steps) : "null"; - return ` - const stepsPanel = document.getElementById('steps-panel'); - const runTitle = document.getElementById('run-title'); - const runStatus = document.getElementById('run-status'); - const runSummary = document.getElementById('run-summary'); - const stepsList = document.getElementById('steps-list'); - let initialSteps = ${initialData}; - if (initialSteps) updateSteps(initialSteps); - - function updateSteps(state) { - if (!stepsPanel || !state) return; - stepsPanel.style.display = 'block'; - if (runTitle) runTitle.textContent = state.title || 'Test Run'; - if (runStatus) { - runStatus.textContent = state.status; - runStatus.className = 'run-status status-' + state.status; - } - if (runSummary) { - runSummary.textContent = state.summary || ''; - runSummary.style.display = state.summary ? 'block' : 'none'; - } - if (stepsList && state.steps) { - stepsList.innerHTML = ''; - for (const step of state.steps) { - const li = document.createElement('li'); - li.className = 'step-item step-' + step.status; - const badge = document.createElement('span'); - badge.className = 'step-badge'; - badge.textContent = step.status === 'passed' ? '\\u2713' - : step.status === 'failed' ? '\\u2717' - : step.status === 'active' ? '\\u25CF' - : '\\u25CB'; - const title = document.createElement('span'); - title.className = 'step-title'; - title.textContent = step.title; - li.appendChild(badge); - li.appendChild(title); - if (step.summary) { - const summary = document.createElement('span'); - summary.className = 'step-summary'; - summary.textContent = step.summary; - li.appendChild(summary); - } - stepsList.appendChild(li); - } - } - }`; -}; - -const stepsStyles = ` - #steps-panel { display: none; margin-bottom: 24px; background: #1e293b; border-radius: 8px; padding: 20px; border: 1px solid #334155 } - #steps-panel h2 { margin: 0 0 4px 0; font-size: 16px; font-weight: 600 } - .run-status { display: inline-block; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; padding: 2px 8px; border-radius: 4px; margin-bottom: 12px } - .status-running { background: #1e3a5f; color: #60a5fa } - .status-passed { background: #14532d; color: #4ade80 } - .status-failed { background: #7f1d1d; color: #f87171 } - #run-summary { font-size: 13px; color: #94a3b8; margin-bottom: 16px } - #steps-list { list-style: none; padding: 0; margin: 0 } - .step-item { display: flex; align-items: baseline; gap: 8px; padding: 6px 0; font-size: 14px; border-top: 1px solid #1e293b } - .step-item:first-child { border-top: none } - .step-badge { flex-shrink: 0; width: 16px; text-align: center; font-size: 13px } - .step-pending .step-badge { color: #64748b } - .step-active .step-badge { color: #60a5fa; animation: pulse 1.5s infinite } - .step-passed .step-badge { color: #4ade80 } - .step-failed .step-badge { color: #f87171 } - .step-title { color: #e2e8f0 } - .step-summary { color: #94a3b8; font-size: 12px; margin-left: auto; max-width: 50%; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap } - @keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.4 } }`; - -export const buildReplayViewerHtml = (options: ReplayViewerOptions): string => { - const source = options.eventsSource; - const isLive = source === "sse"; - - const replaySection = - source !== undefined - ? ` -
      Loading replay…
      - ` - : ""; - - const stepsSection = ` -
      -

      Test Run

      -
      running
      - -
        -
        - `; - - return ` - - - - - ${escapeHtml(options.title)} - ${source !== undefined ? `` : ""} - - - -
        - ${options.bodyHtml ?? ""} - ${stepsSection} - ${replaySection} -
        - -`; -}; diff --git a/packages/recorder/src/viewer-events.ts b/packages/recorder/src/viewer-events.ts deleted file mode 100644 index a4021334..00000000 --- a/packages/recorder/src/viewer-events.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Schema } from "effect"; - -export interface ViewerStepEvent { - readonly stepId: string; - readonly title: string; - readonly status: "pending" | "active" | "passed" | "failed"; - readonly summary: string | undefined; -} - -export const ViewerStepEventSchema = Schema.Struct({ - stepId: Schema.String, - title: Schema.String, - status: Schema.Union([ - Schema.Literal("pending"), - Schema.Literal("active"), - Schema.Literal("passed"), - Schema.Literal("failed"), - ]), - summary: Schema.optional(Schema.String), -}); - -export interface ViewerRunState { - readonly title: string; - readonly status: "running" | "passed" | "failed"; - readonly summary: string | undefined; - readonly steps: readonly ViewerStepEvent[]; -} - -export const ViewerRunStateSchema = Schema.Struct({ - title: Schema.String, - status: Schema.Union([ - Schema.Literal("running"), - Schema.Literal("passed"), - Schema.Literal("failed"), - ]), - summary: Schema.optional(Schema.String), - steps: Schema.Array(ViewerStepEventSchema), -}); diff --git a/packages/recorder/tests/live-view-server.test.ts b/packages/recorder/tests/live-view-server.test.ts deleted file mode 100644 index 3141db8c..00000000 --- a/packages/recorder/tests/live-view-server.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { afterEach, describe, expect, it } from "vite-plus/test"; -import { Effect } from "effect"; -import { startLiveViewServer, type LiveViewHandle } from "../src/live-view-server"; - -const findAvailablePort = async (): Promise => { - const { createServer } = await import("node:http"); - return new Promise((resolve) => { - const server = createServer(); - server.listen(0, "127.0.0.1", () => { - const address = server.address(); - const port = typeof address === "object" && address ? address.port : 0; - server.close(() => resolve(port)); - }); - }); -}; - -const startServer = (port: number, options?: Partial[0]>) => - Effect.runPromise( - startLiveViewServer({ - liveViewUrl: `http://127.0.0.1:${port}`, - getPage: () => undefined, - onEventsCollected: () => {}, - ...options, - }), - ); - -describe("startLiveViewServer", () => { - let server: LiveViewHandle | undefined; - - afterEach(async () => { - if (server) { - await Effect.runPromise(server.close); - server = undefined; - } - }); - - it("starts and serves the HTML viewer at /", async () => { - const port = await findAvailablePort(); - server = await startServer(port); - - const response = await fetch(`http://127.0.0.1:${port}/`); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain("text/html"); - const html = await response.text(); - expect(html).toContain("Expect Live View"); - expect(html).toContain("rrweb-player"); - }); - - it("returns 404 for unknown routes", async () => { - const port = await findAvailablePort(); - server = await startServer(port); - - const response = await fetch(`http://127.0.0.1:${port}/unknown`); - expect(response.status).toBe(404); - }); - - it("serves accumulated events at /latest.json", async () => { - const port = await findAvailablePort(); - server = await startServer(port); - - const response = await fetch(`http://127.0.0.1:${port}/latest.json`); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain("application/json"); - const events = await response.json(); - expect(events).toEqual([]); - }); - - it("exposes the server url", async () => { - const port = await findAvailablePort(); - server = await startServer(port); - - expect(server.url).toBe(`http://127.0.0.1:${port}/`); - }); - - it("opens an SSE connection at /events", async () => { - const port = await findAvailablePort(); - server = await startServer(port); - - const response = await fetch(`http://127.0.0.1:${port}/events`); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain("text/event-stream"); - - response.body?.cancel(); - }); -}); diff --git a/packages/recorder/tests/replay-viewer.test.ts b/packages/recorder/tests/replay-viewer.test.ts deleted file mode 100644 index 00f689f0..00000000 --- a/packages/recorder/tests/replay-viewer.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { describe, expect, it } from "vite-plus/test"; -import { buildReplayViewerHtml } from "../src/replay-viewer"; - -describe("buildReplayViewerHtml", () => { - it("escapes HTML entities in title", () => { - const html = buildReplayViewerHtml({ title: '' }); - expect(html).toContain("<script>alert("xss")</script>"); - expect(html).not.toContain("