From e4227cf6736afdaa0b9c31be0f44ef140b667f50 Mon Sep 17 00:00:00 2001 From: dave Date: Sun, 22 Mar 2026 18:19:23 +0000 Subject: [PATCH] storkit: create 365_story_surface_api_rate_limit_warnings_in_chat --- .claude/settings.json | 73 - .coverage_baseline | 2 - .dockerignore | 11 - .gitignore | 46 - .ignore | 8 - .storkit/.gitignore | 22 - .storkit/README.md | 239 - .storkit/bot.toml.example | 61 - .storkit/problems.md | 28 - .storkit/project.toml | 272 - .storkit/specs/00_CONTEXT.md | 33 - .storkit/specs/functional/SLACK_SETUP.md | 44 - .storkit/specs/functional/UI_LAYOUT.md | 33 - .storkit/specs/functional/UI_UX.md | 474 - .storkit/specs/tech/STACK.md | 130 - .story_kit_port | 1 - CLAUDE.md | 3 - Cargo.lock | 5694 ----------- Cargo.toml | 40 - README.md | 182 - biome.json | 34 - docker/.dockerignore | 11 - docker/Dockerfile | 132 - docker/docker-compose.yml | 118 - docker/entrypoint.sh | 24 - frontend/.gitignore | 14 - frontend/index.html | 14 - frontend/package-lock.json | 8408 ----------------- frontend/package.json | 41 - frontend/playwright.config.ts | 27 - frontend/public/vite.svg | 1 - frontend/src/App.css | 238 - frontend/src/App.test.tsx | 366 - frontend/src/App.tsx | 201 - frontend/src/api/agents.test.ts | 387 - frontend/src/api/agents.ts | 162 - frontend/src/api/client.test.ts | 433 - frontend/src/api/client.ts | 699 -- frontend/src/api/settings.test.ts | 134 - frontend/src/api/settings.ts | 70 - frontend/src/assets/react.svg | 1 - frontend/src/components/AgentPanel.test.tsx | 313 - frontend/src/components/AgentPanel.tsx | 419 - frontend/src/components/Chat.test.tsx | 1459 --- frontend/src/components/Chat.tsx | 1398 --- frontend/src/components/ChatHeader.test.tsx | 314 - frontend/src/components/ChatHeader.tsx | 545 -- frontend/src/components/ChatInput.test.tsx | 279 - frontend/src/components/ChatInput.tsx | 419 - .../components/ChatInputFilePicker.test.tsx | 194 - frontend/src/components/CodeRef.test.tsx | 98 - frontend/src/components/CodeRef.tsx | 118 - frontend/src/components/HelpOverlay.tsx | 158 - .../src/components/LozengeFlyContext.test.tsx | 1329 --- frontend/src/components/LozengeFlyContext.tsx | 445 - frontend/src/components/MessageItem.test.tsx | 137 - frontend/src/components/MessageItem.tsx | 168 - frontend/src/components/ServerLogsPanel.tsx | 246 - .../src/components/SideQuestionOverlay.tsx | 159 - frontend/src/components/StagePanel.test.tsx | 311 - frontend/src/components/StagePanel.tsx | 517 - frontend/src/components/TokenUsagePage.tsx | 440 - .../components/WorkItemDetailPanel.test.tsx | 761 -- .../src/components/WorkItemDetailPanel.tsx | 787 -- .../components/selection/ProjectPathInput.tsx | 170 - .../selection/RecentProjectsList.tsx | 66 - .../selection/SelectionScreen.test.tsx | 136 - .../components/selection/SelectionScreen.tsx | 116 - .../selection/usePathCompletion.test.ts | 461 - .../components/selection/usePathCompletion.ts | 192 - frontend/src/hooks/useChatHistory.test.ts | 275 - frontend/src/hooks/useChatHistory.ts | 117 - frontend/src/main.tsx | 9 - frontend/src/setupTests.ts | 20 - frontend/src/types.ts | 80 - frontend/src/vite-env.d.ts | 14 - frontend/tests/e2e/smoke.spec.ts | 16 - frontend/tsconfig.json | 24 - frontend/vite.config.ts | 51 - frontend/vitest.config.ts | 27 - package-lock.json | 6 - script/release | 238 - script/test | 23 - script/test_coverage | 120 - server/.mcp.json | 8 - server/Cargo.lock | 3022 ------ server/Cargo.toml | 43 - server/build.rs | 50 - server/src/agent_log.rs | 377 - server/src/agents/gates.rs | 426 - server/src/agents/lifecycle.rs | 829 -- server/src/agents/merge.rs | 1696 ---- server/src/agents/mod.rs | 222 - server/src/agents/pool/auto_assign.rs | 1813 ---- server/src/agents/pool/mod.rs | 2232 ----- server/src/agents/pool/pipeline.rs | 1771 ---- server/src/agents/pty.rs | 514 - server/src/agents/runtime/claude_code.rs | 66 - server/src/agents/runtime/gemini.rs | 809 -- server/src/agents/runtime/mod.rs | 159 - server/src/agents/runtime/openai.rs | 704 -- server/src/agents/token_usage.rs | 202 - server/src/config.rs | 880 -- server/src/http/agents.rs | 1209 --- server/src/http/agents_sse.rs | 208 - server/src/http/anthropic.rs | 318 - server/src/http/assets.rs | 148 - server/src/http/chat.rs | 58 - server/src/http/context.rs | 120 - server/src/http/health.rs | 66 - server/src/http/io.rs | 405 - server/src/http/mcp/agent_tools.rs | 731 -- server/src/http/mcp/diagnostics.rs | 735 -- server/src/http/mcp/git_tools.rs | 766 -- server/src/http/mcp/merge_tools.rs | 380 - server/src/http/mcp/mod.rs | 1650 ---- server/src/http/mcp/qa_tools.rs | 293 - server/src/http/mcp/shell_tools.rs | 626 -- server/src/http/mcp/story_tools.rs | 1407 --- server/src/http/mcp/whatsup_tools.rs | 364 - server/src/http/mod.rs | 215 - server/src/http/model.rs | 129 - server/src/http/project.rs | 213 - server/src/http/settings.rs | 369 - server/src/http/workflow/bug_ops.rs | 586 -- server/src/http/workflow/mod.rs | 745 -- server/src/http/workflow/story_ops.rs | 592 -- server/src/http/workflow/test_results.rs | 307 - server/src/http/ws.rs | 1400 --- server/src/io/fs.rs | 1689 ---- server/src/io/mod.rs | 6 - server/src/io/onboarding.rs | 315 - server/src/io/search.rs | 218 - server/src/io/shell.rs | 189 - server/src/io/story_metadata.rs | 542 -- server/src/io/watcher.rs | 1260 --- server/src/llm/chat.rs | 1247 --- server/src/llm/mod.rs | 4 - server/src/llm/prompts.rs | 163 - server/src/llm/providers/anthropic.rs | 868 -- server/src/llm/providers/claude_code.rs | 1306 --- server/src/llm/providers/mod.rs | 3 - server/src/llm/providers/ollama.rs | 267 - server/src/llm/types.rs | 72 - server/src/log_buffer.rs | 424 - server/src/main.rs | 375 - server/src/matrix/bot.rs | 1989 ---- server/src/matrix/commands/ambient.rs | 171 - server/src/matrix/commands/assign.rs | 385 - server/src/matrix/commands/cost.rs | 271 - server/src/matrix/commands/git.rs | 203 - server/src/matrix/commands/help.rs | 113 - server/src/matrix/commands/mod.rs | 470 - server/src/matrix/commands/move_story.rs | 296 - server/src/matrix/commands/overview.rs | 380 - server/src/matrix/commands/show.rs | 201 - server/src/matrix/commands/status.rs | 354 - server/src/matrix/commands/whatsup.rs | 550 -- server/src/matrix/config.rs | 815 -- server/src/matrix/delete.rs | 384 - server/src/matrix/htop.rs | 596 -- server/src/matrix/mod.rs | 90 - server/src/matrix/notifications.rs | 398 - server/src/matrix/rebuild.rs | 145 - server/src/matrix/reset.rs | 170 - server/src/matrix/start.rs | 391 - server/src/matrix/transport_impl.rs | 96 - server/src/rebuild.rs | 104 - server/src/slack.rs | 1463 --- server/src/state.rs | 36 - server/src/store.rs | 183 - server/src/transport.rs | 97 - server/src/whatsapp.rs | 1414 --- server/src/workflow.rs | 399 - server/src/worktree.rs | 884 -- 175 files changed, 83945 deletions(-) delete mode 100644 .claude/settings.json delete mode 100644 .coverage_baseline delete mode 100644 .dockerignore delete mode 100644 .gitignore delete mode 100644 .ignore delete mode 100644 .storkit/.gitignore delete mode 100644 .storkit/README.md delete mode 100644 .storkit/bot.toml.example delete mode 100644 .storkit/problems.md delete mode 100644 .storkit/project.toml delete mode 100644 .storkit/specs/00_CONTEXT.md delete mode 100644 .storkit/specs/functional/SLACK_SETUP.md delete mode 100644 .storkit/specs/functional/UI_LAYOUT.md delete mode 100644 .storkit/specs/functional/UI_UX.md delete mode 100644 .storkit/specs/tech/STACK.md delete mode 100644 .story_kit_port delete mode 100644 CLAUDE.md delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml delete mode 100644 README.md delete mode 100644 biome.json delete mode 100644 docker/.dockerignore delete mode 100644 docker/Dockerfile delete mode 100644 docker/docker-compose.yml delete mode 100755 docker/entrypoint.sh delete mode 100644 frontend/.gitignore delete mode 100644 frontend/index.html delete mode 100644 frontend/package-lock.json delete mode 100644 frontend/package.json delete mode 100644 frontend/playwright.config.ts delete mode 100644 frontend/public/vite.svg delete mode 100644 frontend/src/App.css delete mode 100644 frontend/src/App.test.tsx delete mode 100644 frontend/src/App.tsx delete mode 100644 frontend/src/api/agents.test.ts delete mode 100644 frontend/src/api/agents.ts delete mode 100644 frontend/src/api/client.test.ts delete mode 100644 frontend/src/api/client.ts delete mode 100644 frontend/src/api/settings.test.ts delete mode 100644 frontend/src/api/settings.ts delete mode 100644 frontend/src/assets/react.svg delete mode 100644 frontend/src/components/AgentPanel.test.tsx delete mode 100644 frontend/src/components/AgentPanel.tsx delete mode 100644 frontend/src/components/Chat.test.tsx delete mode 100644 frontend/src/components/Chat.tsx delete mode 100644 frontend/src/components/ChatHeader.test.tsx delete mode 100644 frontend/src/components/ChatHeader.tsx delete mode 100644 frontend/src/components/ChatInput.test.tsx delete mode 100644 frontend/src/components/ChatInput.tsx delete mode 100644 frontend/src/components/ChatInputFilePicker.test.tsx delete mode 100644 frontend/src/components/CodeRef.test.tsx delete mode 100644 frontend/src/components/CodeRef.tsx delete mode 100644 frontend/src/components/HelpOverlay.tsx delete mode 100644 frontend/src/components/LozengeFlyContext.test.tsx delete mode 100644 frontend/src/components/LozengeFlyContext.tsx delete mode 100644 frontend/src/components/MessageItem.test.tsx delete mode 100644 frontend/src/components/MessageItem.tsx delete mode 100644 frontend/src/components/ServerLogsPanel.tsx delete mode 100644 frontend/src/components/SideQuestionOverlay.tsx delete mode 100644 frontend/src/components/StagePanel.test.tsx delete mode 100644 frontend/src/components/StagePanel.tsx delete mode 100644 frontend/src/components/TokenUsagePage.tsx delete mode 100644 frontend/src/components/WorkItemDetailPanel.test.tsx delete mode 100644 frontend/src/components/WorkItemDetailPanel.tsx delete mode 100644 frontend/src/components/selection/ProjectPathInput.tsx delete mode 100644 frontend/src/components/selection/RecentProjectsList.tsx delete mode 100644 frontend/src/components/selection/SelectionScreen.test.tsx delete mode 100644 frontend/src/components/selection/SelectionScreen.tsx delete mode 100644 frontend/src/components/selection/usePathCompletion.test.ts delete mode 100644 frontend/src/components/selection/usePathCompletion.ts delete mode 100644 frontend/src/hooks/useChatHistory.test.ts delete mode 100644 frontend/src/hooks/useChatHistory.ts delete mode 100644 frontend/src/main.tsx delete mode 100644 frontend/src/setupTests.ts delete mode 100644 frontend/src/types.ts delete mode 100644 frontend/src/vite-env.d.ts delete mode 100644 frontend/tests/e2e/smoke.spec.ts delete mode 100644 frontend/tsconfig.json delete mode 100644 frontend/vite.config.ts delete mode 100644 frontend/vitest.config.ts delete mode 100644 package-lock.json delete mode 100755 script/release delete mode 100755 script/test delete mode 100755 script/test_coverage delete mode 100644 server/.mcp.json delete mode 100644 server/Cargo.lock delete mode 100644 server/Cargo.toml delete mode 100644 server/build.rs delete mode 100644 server/src/agent_log.rs delete mode 100644 server/src/agents/gates.rs delete mode 100644 server/src/agents/lifecycle.rs delete mode 100644 server/src/agents/merge.rs delete mode 100644 server/src/agents/mod.rs delete mode 100644 server/src/agents/pool/auto_assign.rs delete mode 100644 server/src/agents/pool/mod.rs delete mode 100644 server/src/agents/pool/pipeline.rs delete mode 100644 server/src/agents/pty.rs delete mode 100644 server/src/agents/runtime/claude_code.rs delete mode 100644 server/src/agents/runtime/gemini.rs delete mode 100644 server/src/agents/runtime/mod.rs delete mode 100644 server/src/agents/runtime/openai.rs delete mode 100644 server/src/agents/token_usage.rs delete mode 100644 server/src/config.rs delete mode 100644 server/src/http/agents.rs delete mode 100644 server/src/http/agents_sse.rs delete mode 100644 server/src/http/anthropic.rs delete mode 100644 server/src/http/assets.rs delete mode 100644 server/src/http/chat.rs delete mode 100644 server/src/http/context.rs delete mode 100644 server/src/http/health.rs delete mode 100644 server/src/http/io.rs delete mode 100644 server/src/http/mcp/agent_tools.rs delete mode 100644 server/src/http/mcp/diagnostics.rs delete mode 100644 server/src/http/mcp/git_tools.rs delete mode 100644 server/src/http/mcp/merge_tools.rs delete mode 100644 server/src/http/mcp/mod.rs delete mode 100644 server/src/http/mcp/qa_tools.rs delete mode 100644 server/src/http/mcp/shell_tools.rs delete mode 100644 server/src/http/mcp/story_tools.rs delete mode 100644 server/src/http/mcp/whatsup_tools.rs delete mode 100644 server/src/http/mod.rs delete mode 100644 server/src/http/model.rs delete mode 100644 server/src/http/project.rs delete mode 100644 server/src/http/settings.rs delete mode 100644 server/src/http/workflow/bug_ops.rs delete mode 100644 server/src/http/workflow/mod.rs delete mode 100644 server/src/http/workflow/story_ops.rs delete mode 100644 server/src/http/workflow/test_results.rs delete mode 100644 server/src/http/ws.rs delete mode 100644 server/src/io/fs.rs delete mode 100644 server/src/io/mod.rs delete mode 100644 server/src/io/onboarding.rs delete mode 100644 server/src/io/search.rs delete mode 100644 server/src/io/shell.rs delete mode 100644 server/src/io/story_metadata.rs delete mode 100644 server/src/io/watcher.rs delete mode 100644 server/src/llm/chat.rs delete mode 100644 server/src/llm/mod.rs delete mode 100644 server/src/llm/prompts.rs delete mode 100644 server/src/llm/providers/anthropic.rs delete mode 100644 server/src/llm/providers/claude_code.rs delete mode 100644 server/src/llm/providers/mod.rs delete mode 100644 server/src/llm/providers/ollama.rs delete mode 100644 server/src/llm/types.rs delete mode 100644 server/src/log_buffer.rs delete mode 100644 server/src/main.rs delete mode 100644 server/src/matrix/bot.rs delete mode 100644 server/src/matrix/commands/ambient.rs delete mode 100644 server/src/matrix/commands/assign.rs delete mode 100644 server/src/matrix/commands/cost.rs delete mode 100644 server/src/matrix/commands/git.rs delete mode 100644 server/src/matrix/commands/help.rs delete mode 100644 server/src/matrix/commands/mod.rs delete mode 100644 server/src/matrix/commands/move_story.rs delete mode 100644 server/src/matrix/commands/overview.rs delete mode 100644 server/src/matrix/commands/show.rs delete mode 100644 server/src/matrix/commands/status.rs delete mode 100644 server/src/matrix/commands/whatsup.rs delete mode 100644 server/src/matrix/config.rs delete mode 100644 server/src/matrix/delete.rs delete mode 100644 server/src/matrix/htop.rs delete mode 100644 server/src/matrix/mod.rs delete mode 100644 server/src/matrix/notifications.rs delete mode 100644 server/src/matrix/rebuild.rs delete mode 100644 server/src/matrix/reset.rs delete mode 100644 server/src/matrix/start.rs delete mode 100644 server/src/matrix/transport_impl.rs delete mode 100644 server/src/rebuild.rs delete mode 100644 server/src/slack.rs delete mode 100644 server/src/state.rs delete mode 100644 server/src/store.rs delete mode 100644 server/src/transport.rs delete mode 100644 server/src/whatsapp.rs delete mode 100644 server/src/workflow.rs delete mode 100644 server/src/worktree.rs diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 36aa5f7..0000000 --- a/.claude/settings.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "enabledMcpjsonServers": ["storkit"], - "permissions": { - "allow": [ - "Bash(./server/target/debug/storkit:*)", - "Bash(./target/debug/storkit:*)", - "Bash(STORKIT_PORT=*)", - "Bash(cargo build:*)", - "Bash(cargo check:*)", - "Bash(cargo clippy:*)", - "Bash(cargo doc:*)", - "Bash(cargo llvm-cov:*)", - "Bash(cargo nextest run:*)", - "Bash(cargo run:*)", - "Bash(cargo test:*)", - "Bash(cargo watch:*)", - "Bash(cd *)", - "Bash(claude:*)", - "Bash(curl:*)", - "Bash(echo:*)", - "Bash(env:*)", - "Bash(git *)", - "Bash(grep:*)", - "Bash(kill *)", - "Bash(ls *)", - "Bash(lsof *)", - "Bash(mkdir *)", - "Bash(mv *)", - "Bash(npm run build:*)", - "Bash(npx @biomejs/biome check:*)", - "Bash(npx @playwright/test test:*)", - "Bash(npx biome check:*)", - "Bash(npx playwright test:*)", - "Bash(npx tsc:*)", - "Bash(npx vitest:*)", - "Bash(pnpm add:*)", - "Bash(pnpm build:*)", - "Bash(pnpm dev:*)", - "Bash(pnpm install:*)", - "Bash(pnpm run build:*)", - "Bash(pnpm run test:*)", - "Bash(pnpm test:*)", - "Bash(printf:*)", - "Bash(ps *)", - "Bash(python3:*)", - "Bash(pwd *)", - "Bash(rm *)", - "Bash(sleep *)", - "Bash(touch *)", - "Bash(xargs:*)", - "WebFetch(domain:crates.io)", - "WebFetch(domain:docs.rs)", - "WebFetch(domain:github.com)", - "WebFetch(domain:portkey.ai)", - "WebFetch(domain:www.shuttle.dev)", - "WebSearch", - "mcp__storkit__*", - "Edit", - "Write", - "Bash(find *)", - "Bash(sqlite3 *)", - "Bash(cat <<:*)", - "Bash(cat <<'ENDJSON:*)", - "Bash(make release:*)", - "Bash(npm test:*)", - "Bash(head *)", - "Bash(tail *)", - "Bash(wc *)", - "Bash(npx vite:*)", - "Bash(npm run dev:*)" - ] - } -} diff --git a/.coverage_baseline b/.coverage_baseline deleted file mode 100644 index 71d9abb..0000000 --- a/.coverage_baseline +++ /dev/null @@ -1,2 +0,0 @@ -60.00 -65.21 diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index ec606cf..0000000 --- a/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -# Docker build context exclusions -**/target/ -**/node_modules/ -frontend/dist/ -.storkit/worktrees/ -.storkit/logs/ -.storkit/work/6_archived/ -.git/ -*.swp -*.swo -.DS_Store diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f9662e4..0000000 --- a/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -# Claude Code -.claude/settings.local.json -.mcp.json - -# Local environment (secrets) -.env - -# App specific (root-level; storkit subdirectory patterns live in .storkit/.gitignore) -store.json -.storkit_port - -# Rust stuff -target - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -frontend/node_modules -frontend/dist -frontend/dist-ssr -frontend/test-results -frontend/serve -frontend/*.local -server/target - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store - -# Vite/Vitest cache -.vite/ -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? -/test-results/.last-run.json diff --git a/.ignore b/.ignore deleted file mode 100644 index 518d062..0000000 --- a/.ignore +++ /dev/null @@ -1,8 +0,0 @@ -# Prevents cargo watch from restarting the server on non-Rust file changes -frontend/ -node_modules/ -.claude/ -.git/ -.storkit/ -store.json -.storkit_port diff --git a/.storkit/.gitignore b/.storkit/.gitignore deleted file mode 100644 index a151743..0000000 --- a/.storkit/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Bot config (contains credentials) -bot.toml - -# Matrix SDK state store -matrix_store/ -matrix_device_id -matrix_history.json - -# Agent worktrees and merge workspace (managed by the server, not tracked in git) -worktrees/ -merge_workspace/ - -# Intermediate pipeline stages (transient, not committed per spike 92) -work/2_current/ -work/3_qa/ -work/4_merge/ - -# Coverage reports (generated by cargo-llvm-cov, not tracked in git) -coverage/ - -# Token usage log (generated at runtime, contains cost data) -token_usage.jsonl diff --git a/.storkit/README.md b/.storkit/README.md deleted file mode 100644 index ccb3bb2..0000000 --- a/.storkit/README.md +++ /dev/null @@ -1,239 +0,0 @@ -# Story Kit: The Story-Driven Test Workflow (SDTW) - -**Target Audience:** Large Language Models (LLMs) acting as Senior Engineers. -**Goal:** To maintain long-term project coherence, prevent context window exhaustion, and ensure high-quality, testable code generation in large software projects. - ---- - -## 0. First Steps (For New LLM Sessions) - -When you start a new session with this project: - -1. **Check for MCP Tools:** Read `.mcp.json` to discover the MCP server endpoint. Then list available tools by calling: - ```bash - curl -s "$(jq -r '.mcpServers["storkit"].url' .mcp.json)" \ - -H 'Content-Type: application/json' \ - -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' - ``` - This returns the full tool catalog (create stories, spawn agents, record tests, manage worktrees, etc.). Familiarize yourself with the available tools before proceeding. These tools allow you to directly manipulate the workflow and spawn subsidiary agents without manual file manipulation. -2. **Read Context:** Check `.story_kit/specs/00_CONTEXT.md` for high-level project goals. -3. **Read Stack:** Check `.story_kit/specs/tech/STACK.md` for technical constraints and patterns. -4. **Check Work Items:** Look at `.story_kit/work/1_backlog/` and `.story_kit/work/2_current/` to see what work is pending. - - ---- - -## 1. The Philosophy - -We treat the codebase as the implementation of a **"Living Specification."** driven by **User Stories** -Instead of ephemeral chat prompts ("Fix this", "Add that"), we work through persistent artifacts. -* **Stories** define the *Change*. -* **Tests** define the *Truth*. -* **Code** defines the *Reality*. - -**The Golden Rule:** You are not allowed to write code until the Acceptance Criteria are captured in the story. - ---- - -## 1.5 MCP Tools - -Agents have programmatic access to the workflow via MCP tools served at `POST /mcp`. The project `.mcp.json` registers this endpoint automatically so Claude Code sessions and spawned agents can call tools like `create_story`, `validate_stories`, `list_upcoming`, `get_story_todos`, `record_tests`, `ensure_acceptance`, `start_agent`, `stop_agent`, `list_agents`, and `get_agent_output` without parsing English instructions. - -**To discover what tools are available:** Check `.mcp.json` for the server endpoint, then use the MCP protocol to list available tools. - ---- - -## 2. Directory Structure - -```text -project_root/ - .mcp.json # MCP server configuration (if MCP tools are available) - .story_kit/ - ├── README.md # This document - ├── project.toml # Agent configuration (roles, models, prompts) - ├── work/ # Unified work item pipeline (stories, bugs, spikes) - │ ├── 1_backlog/ # New work items awaiting implementation - │ ├── 2_current/ # Work in progress - │ ├── 3_qa/ # QA review - │ ├── 4_merge/ # Ready to merge to master - │ ├── 5_done/ # Merged and completed (auto-swept to 6_archived after 4 hours) - │ └── 6_archived/ # Long-term archive - ├── worktrees/ # Agent worktrees (managed by the server) - ├── specs/ # Minimal guardrails (context + stack) - │ ├── 00_CONTEXT.md # High-level goals, domain definition, and glossary - │ ├── tech/ # Implementation details (Stack, Architecture, Constraints) - │ │ └── STACK.md # The "Constitution" (Languages, Libs, Patterns) - │ └── functional/ # Domain logic (Platform-agnostic behavior) - │ └── ... - └── src/ # The Code -``` - -### Work Items - -All work items (stories, bugs, spikes) live in the same `work/` pipeline. Items are named: `{id}_{type}_{slug}.md` - -* Stories: `57_story_live_test_gate_updates.md` -* Bugs: `4_bug_run_button_does_not_start_agent.md` -* Spikes: `61_spike_filesystem_watcher_architecture.md` - -Items move through stages by moving the file between directories: - -`1_backlog` → `2_current` → `3_qa` → `4_merge` → `5_done` → `6_archived` - -Items in `5_done` are auto-swept to `6_archived` after 4 hours by the server. - -### Filesystem Watcher - -The server watches `.story_kit/work/` for changes. When a file is created, moved, or modified, the watcher auto-commits with a deterministic message and broadcasts a WebSocket notification to the frontend. This means: - -* MCP tools only need to write/move files — the watcher handles git commits -* IDE drag-and-drop works (drag a story from `1_backlog/` to `2_current/`) -* The frontend updates automatically without manual refresh - ---- - -## 3. The Cycle (The "Loop") - -When the user asks for a feature, follow this 4-step loop strictly: - -### Step 1: The Story (Ingest) -* **User Input:** "I want the robot to dance." -* **Action:** Create a story via MCP tool `create_story` (guarantees correct front matter and auto-assigns the story number). -* **Front Matter (Required):** Every work item file MUST begin with YAML front matter containing a `name` field: - ```yaml - --- - name: Short Human-Readable Story Name - --- - ``` -* **Move to Current:** Once the story is validated and ready for coding, move it to `work/2_current/`. -* **Tracking:** Mark Acceptance Criteria as tested directly in the story file as tests are completed. -* **Content:** - * **User Story:** "As a user, I want..." - * **Acceptance Criteria:** Bullet points of observable success. - * **Out of scope:** Things that are out of scope so that the LLM doesn't go crazy -* **Story Quality (INVEST):** Stories should be Independent, Negotiable, Valuable, Estimable, Small, and Testable. -* **Git:** The `start_agent` MCP tool automatically creates a worktree under `.story_kit/worktrees/`, checks out a feature branch, moves the story to `work/2_current/`, and spawns the agent. No manual branch or worktree creation is needed. - -### Step 2: The Implementation (Code) -* **Action:** Write the code to satisfy the approved tests and Acceptance Criteria. -* **Constraint:** adhere strictly to `specs/tech/STACK.md` (e.g., if it forbids certain patterns, you must not use them). -* **Full-Stack Completion:** Every story must be completed across all components of the stack. If a feature touches the backend, frontend, and API layer, all three must be fully implemented and working end-to-end before the story can be accepted. Partial implementations (e.g., backend logic with no frontend wiring, or UI scaffolding with no real data) do not satisfy acceptance criteria. - -### Step 3: Verification (Close) -* **Action:** For each Acceptance Criterion in the story, write a failing test (red), mark the criterion as tested, make the test pass (green), and refactor if needed. Keep only one failing test at a time. -* **Action:** Run compilation and make sure it succeeds without errors. Consult `specs/tech/STACK.md` and run all required linters listed there (treat warnings as errors). Run tests and make sure they all pass before proceeding. Ask questions here if needed. -* **Action:** Do not accept stories yourself. Ask the user if they accept the story. If they agree, move the story file to `work/5_done/`. -* **Move to Done:** After acceptance, move the story from `work/2_current/` (or `work/4_merge/`) to `work/5_done/`. -* **Action:** When the user accepts: - 1. Move the story file to `work/5_done/` - 2. Commit both changes to the feature branch - 3. Perform the squash merge: `git merge --squash feature/story-name` - 4. Commit to master with a comprehensive commit message - 5. Delete the feature branch: `git branch -D feature/story-name` -* **Important:** Do NOT mark acceptance criteria as complete before user acceptance. Only mark them complete when the user explicitly accepts the story. - -**CRITICAL - NO SUMMARY DOCUMENTS:** -* **NEVER** create a separate summary document (e.g., `STORY_XX_SUMMARY.md`, `IMPLEMENTATION_NOTES.md`, etc.) -* **NEVER** write terminal output to a markdown file for "documentation purposes" -* Tests are the primary source of truth. Keep test coverage and Acceptance Criteria aligned after each story. -* If you find yourself typing `cat << 'EOF' > SUMMARY.md` or similar, **STOP IMMEDIATELY**. -* The only files that should exist after story completion: - * Updated code in `src/` - * Updated guardrails in `specs/` (if needed) - * Archived work item in `work/5_done/` (server auto-sweeps to `work/6_archived/` after 4 hours) - ---- - - -## 3.5. Bug Workflow (Simplified Path) - -Not everything needs to be a full story. Simple bugs can skip the story process: - -### When to Use Bug Workflow -* Defects in existing functionality (not new features) -* State inconsistencies or data corruption -* UI glitches that don't require spec changes -* Performance issues with known fixes - -### Bug Process -1. **Document Bug:** Create a bug file in `work/1_backlog/` named `{id}_bug_{slug}.md` with: - * **Symptom:** What the user observes - * **Root Cause:** Technical explanation (if known) - * **Reproduction Steps:** How to trigger the bug - * **Proposed Fix:** Brief technical approach - * **Workaround:** Temporary solution if available -2. **Start an Agent:** Use the `start_agent` MCP tool to create a worktree and spawn an agent for the bug fix. -3. **Write a Failing Test:** Before fixing the bug, write a test that reproduces it (red). This proves the bug exists and prevents regression. -4. **Fix the Bug:** Make minimal code changes to make the test pass (green). -5. **User Testing:** Let the user verify the fix in the worktree before merging. Do not proceed until they confirm. -6. **Archive & Merge:** Move the bug file to `work/5_done/`, squash merge to master, delete the worktree and branch. -7. **No Guardrail Update Needed:** Unless the bug reveals a missing constraint - -### Bug vs Story vs Spike -* **Bug:** Existing functionality is broken → Fix it -* **Story:** New functionality is needed → Test it, then build it -* **Spike:** Uncertainty/feasibility discovery → Run spike workflow - ---- - -## 3.6. Spike Workflow (Research Path) - -Not everything needs a story or bug fix. Spikes are time-boxed investigations to reduce uncertainty. - -### When to Use a Spike -* Unclear root cause or feasibility -* Need to compare libraries/encoders/formats -* Need to validate performance constraints - -### Spike Process -1. **Document Spike:** Create a spike file in `work/1_backlog/` named `{id}_spike_{slug}.md` with: - * **Question:** What you need to answer - * **Hypothesis:** What you expect to be true - * **Timebox:** Strict limit for the research - * **Investigation Plan:** Steps/tools to use - * **Findings:** Evidence and observations - * **Recommendation:** Next step (Story, Bug, or No Action) -2. **Execute Research:** Stay within the timebox. No production code changes. -3. **Escalate if Needed:** If implementation is required, open a Story or Bug and follow that workflow. -4. **Archive:** Move the spike file to `work/5_done/`. - -### Spike Output -* Decision and evidence, not production code -* Specs updated only if the spike changes system truth - ---- - -## 4. Context Reset Protocol - -When the LLM context window fills up (or the chat gets slow/confused): -1. **Stop Coding.** -2. **Instruction:** Tell the user to open a new chat. -3. **Handoff:** The only context the new LLM needs is in the `specs/` folder and `.mcp.json`. - * *Prompt for New Session:* "I am working on Project X. Read `.mcp.json` to discover available tools, then read `specs/00_CONTEXT.md` and `specs/tech/STACK.md`. Then look at `work/1_backlog/` and `work/2_current/` to see what is pending." - - ---- - -## 5. Setup Instructions (For the LLM) - -If a user hands you this document and says "Apply this process to my project": - -1. **Check for MCP Tools:** Look for `.mcp.json` in the project root. If it exists, you have programmatic access to workflow tools and agent spawning capabilities. -2. **Analyze the Request:** Ask for the high-level goal ("What are we building?") and the tech preferences ("Rust or Python?"). -3. **Git Check:** Check if the directory is a git repository (`git status`). If not, run `git init`. -4. **Scaffold:** Run commands to create the `work/` and `specs/` folders with the 6-stage pipeline (`work/1_backlog/` through `work/6_archived/`). -5. **Draft Context:** Write `specs/00_CONTEXT.md` based on the user's answer. -6. **Draft Stack:** Write `specs/tech/STACK.md` based on best practices for that language. -7. **Wait:** Ask the user for "Story #1". - ---- - -## 6. Code Quality - -**MANDATORY:** Before completing Step 3 (Verification) of any story, you MUST run all applicable linters, formatters, and test suites and fix ALL errors and warnings. Zero tolerance for warnings or errors. - -**AUTO-RUN CHECKS:** Always run the required lint/test/build checks as soon as relevant changes are made. Do not ask for permission to run them—run them automatically and fix any failures. - -**ALWAYS FIX DIAGNOSTICS:** At every stage, you must proactively fix all errors and warnings without waiting for user confirmation. Do not pause to ask whether to fix diagnostics—fix them immediately as part of the workflow. - -**Consult `specs/tech/STACK.md`** for the specific tools, commands, linter configurations, and quality gates for this project. The STACK file is the single source of truth for what must pass before a story can be accepted. diff --git a/.storkit/bot.toml.example b/.storkit/bot.toml.example deleted file mode 100644 index 7e104a2..0000000 --- a/.storkit/bot.toml.example +++ /dev/null @@ -1,61 +0,0 @@ -homeserver = "https://matrix.example.com" -username = "@botname:example.com" -password = "your-bot-password" - -# List one or more rooms to listen in. Use a single-element list for one room. -room_ids = ["!roomid:example.com"] - -# Optional: the deprecated single-room key is still accepted for backwards compat. -# room_id = "!roomid:example.com" - -allowed_users = ["@youruser:example.com"] -enabled = false - -# Maximum conversation turns to remember per room (default: 20). -# history_size = 20 - -# Rooms where the bot responds to all messages (not just addressed ones). -# This list is updated automatically when users toggle ambient mode at runtime. -# ambient_rooms = ["!roomid:example.com"] - -# ── WhatsApp Business API ────────────────────────────────────────────── -# Set transport = "whatsapp" to use WhatsApp instead of Matrix. -# The webhook endpoint will be available at /webhook/whatsapp. -# You must configure this URL in the Meta Developer Dashboard. -# -# transport = "whatsapp" -# whatsapp_phone_number_id = "123456789012345" -# whatsapp_access_token = "EAAx..." -# whatsapp_verify_token = "my-secret-verify-token" -# -# ── 24-hour messaging window & notification templates ───────────────── -# WhatsApp only allows free-form text messages within 24 hours of the last -# inbound message from a user. For proactive pipeline notifications sent -# after the window expires, an approved Meta message template is used. -# -# Register the template in the Meta Business Manager: -# 1. Go to Business Settings → WhatsApp → Message Templates → Create. -# 2. Category: UTILITY -# 3. Template name: pipeline_notification (or your chosen name below) -# 4. Language: English (en_US) -# 5. Body text (example): -# Story *{{1}}* has moved to *{{2}}*. -# Where {{1}} = story name, {{2}} = pipeline stage. -# 6. Submit for review. Meta typically approves utility templates within -# minutes; transactional categories may take longer. -# -# Once approved, set the name below (default: "pipeline_notification"): -# whatsapp_notification_template = "pipeline_notification" - -# ── Slack Bot API ───────────────────────────────────────────────────── -# Set transport = "slack" to use Slack instead of Matrix. -# The webhook endpoint will be available at /webhook/slack. -# Configure this URL in the Slack App → Event Subscriptions → Request URL. -# -# Required Slack App scopes: chat:write, chat:update -# Subscribe to bot events: message.channels, message.groups, message.im -# -# transport = "slack" -# slack_bot_token = "xoxb-..." -# slack_signing_secret = "your-signing-secret" -# slack_channel_ids = ["C01ABCDEF"] diff --git a/.storkit/problems.md b/.storkit/problems.md deleted file mode 100644 index 8d294bd..0000000 --- a/.storkit/problems.md +++ /dev/null @@ -1,28 +0,0 @@ -# Problems - -Recurring issues observed during pipeline operation. Review periodically and create stories for systemic problems. - -## 2026-03-18: Stories graduating to "done" with empty merges (7 of 10) - -Pipeline allows stories to move through coding → QA → merge → done without any actual code changes landing on master. The squash-merge produces an empty diff but the pipeline still marks the story as done. Affected stories: 247, 273, 274, 278, 279, 280, 92. Only 266, 271, 277, and 281 actually shipped code. Root cause: no check that the merge commit contains a non-empty diff. Filed bug 283 for the manual_qa gate issue specifically, but the empty-merge-to-done problem is broader and needs its own fix. - -## 2026-03-18: Agent committed directly to master instead of worktree - -Multiple agents have committed directly to master instead of their worktree/feature branch: - -- Commit `5f4591f` ("fix: update should_commit_stage test to match 5_done") — likely mergemaster -- Commit `a32cfbd` ("Add bot-level command registry with help command") — story 285 coder committed code + Cargo.lock directly to master - -Agents should only commit to their feature branch or merge-queue branch, never to master directly. Suspect agents are running `git commit` in the project root instead of the worktree directory. This can also revert uncommitted fixes on master (e.g. project.toml pkill fix was overwritten). Frequency: at least 2 confirmed cases. This is a recurring and serious problem — needs a guard in the server or agent prompts. - -## 2026-03-19: Auto-assign re-assigns mergemaster to failed merge stories in a loop - -After bug 295 fix (`auto_assign_available_work` after every pipeline advance), mergemaster gets re-assigned to stories that already have a merge failure flag. Story 310 had an empty diff merge failure — mergemaster correctly reported the failure, but auto-assign immediately re-assigned mergemaster to the same story, creating an infinite retry loop. The auto-assign logic needs to check for the `merge_failure` front matter flag before re-assigning agents to stories in `4_merge/`. - -## 2026-03-19: Coder produces no code (complete ghost — story 310) - -Story 310 (Bot delete command) went through the full pipeline — coder session ran, passed QA/gates, moved to merge — but the coder produced zero code. No commits on the feature branch, no commits on master. The entire agent session was a no-op. This is different from the "committed to master instead of worktree" problem — in this case, the coder simply did nothing. Need to investigate the coder logs to understand what happened. The empty-diff merge check would catch this at merge time, but ideally the server should detect "coder finished with no commits on feature branch" at the gate-check stage and fail early. - -## 2026-03-19: Auto-assign assigns mergemaster to coding-stage stories - -Auto-assign picked mergemaster for story 310 which was in `2_current/`. Mergemaster should only work on stories in `4_merge/`. The `auto_assign_available_work` function doesn't enforce that the agent's configured stage matches the pipeline stage of the story it's being assigned to. Story 279 (auto-assign respects agent stage from front matter) was supposed to fix this, but the check may only apply to front-matter preferences, not the fallback assignment path. diff --git a/.storkit/project.toml b/.storkit/project.toml deleted file mode 100644 index becc6bc..0000000 --- a/.storkit/project.toml +++ /dev/null @@ -1,272 +0,0 @@ -# Project-wide default QA mode: "server", "agent", or "human". -# Per-story `qa` front matter overrides this setting. -default_qa = "server" - -# Default model for coder agents. Only agents with this model are auto-assigned. -# Opus coders are reserved for explicit per-story `agent:` front matter requests. -default_coder_model = "sonnet" - -# Maximum concurrent coder agents. Stories wait in 2_current/ when all slots are full. -max_coders = 3 - -# Maximum retries per story per pipeline stage before marking as blocked. -# Set to 0 to disable retry limits. -max_retries = 2 - -[[component]] -name = "frontend" -path = "frontend" -setup = ["npm install", "npm run build"] -teardown = [] - -[[component]] -name = "server" -path = "." -setup = ["mkdir -p frontend/dist", "cargo check"] -teardown = [] - -[[agent]] -name = "coder-1" -stage = "coder" -role = "Full-stack engineer. Implements features across all components." -model = "sonnet" -max_turns = 50 -max_budget_usd = 5.00 -prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix." -system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing." - -[[agent]] -name = "coder-2" -stage = "coder" -role = "Full-stack engineer. Implements features across all components." -model = "sonnet" -max_turns = 50 -max_budget_usd = 5.00 -prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix." -system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing." - -[[agent]] -name = "coder-3" -stage = "coder" -role = "Full-stack engineer. Implements features across all components." -model = "sonnet" -max_turns = 50 -max_budget_usd = 5.00 -prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix." -system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing." - -[[agent]] -name = "qa-2" -stage = "qa" -role = "Reviews coder work in worktrees: runs quality gates, generates testing plans, and reports findings." -model = "sonnet" -max_turns = 40 -max_budget_usd = 4.00 -prompt = """You are the QA agent for story {{story_id}}. Your job is to review the coder's work in the worktree and produce a structured QA report. - -Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. - -## Your Workflow - -### 1. Code Quality Scan -- Run `git diff master...HEAD --stat` to see what files changed -- Run `git diff master...HEAD` to review the actual changes for obvious coding mistakes (unused imports, dead code, unhandled errors, hardcoded values) -- Run `cargo clippy --all-targets --all-features` and note any warnings -- If a `frontend/` directory exists: - - Run `npm run build` and note any TypeScript errors - - Run `npx @biomejs/biome check src/` and note any linting issues - -### 2. Test Verification -- Run `cargo test` and verify all tests pass -- If `frontend/` exists: run `npm test` and verify all frontend tests pass -- Review test quality: look for tests that are trivial or don't assert meaningful behavior - -### 3. Manual Testing Support -- Build the server: run `cargo build` and note success/failure -- If build succeeds: find a free port (try 3010-3020) and attempt to start the server -- Generate a testing plan including: - - URL to visit in the browser - - Things to check in the UI - - curl commands to exercise relevant API endpoints -- Kill the test server when done: `pkill -f 'target.*storkit' || true` (NEVER use `pkill -f storkit` — it kills the vite dev server) - -### 4. Produce Structured Report -Print your QA report to stdout before your process exits. The server will automatically run acceptance gates. Use this format: - -``` -## QA Report for {{story_id}} - -### Code Quality -- clippy: PASS/FAIL (details) -- TypeScript build: PASS/FAIL/SKIP (details) -- Biome lint: PASS/FAIL/SKIP (details) -- Code review findings: (list any issues found, or "None") - -### Test Verification -- cargo test: PASS/FAIL (N tests) -- npm test: PASS/FAIL/SKIP (N tests) -- Test quality issues: (list any trivial/weak tests, or "None") - -### Manual Testing Plan -- Server URL: http://localhost:PORT (or "Build failed") -- Pages to visit: (list) -- Things to check: (list) -- curl commands: (list) - -### Overall: PASS/FAIL -``` - -## Rules -- Do NOT modify any code — read-only review only -- If the server fails to start, still provide the testing plan with curl commands -- The server automatically runs acceptance gates when your process exits""" -system_prompt = "You are a QA agent. Your job is read-only: review code quality, run tests, try to start the server, and produce a structured QA report. Do not modify code. The server automatically runs acceptance gates when your process exits." - -[[agent]] -name = "coder-opus" -stage = "coder" -role = "Senior full-stack engineer for complex tasks. Implements features across all components." -model = "opus" -max_turns = 80 -max_budget_usd = 20.00 -prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. The story details are in your prompt above. Follow the SDTW process through implementation and verification (Steps 1-3). The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop. If the user asks to review your changes, tell them to run: cd \"{{worktree_path}}\" && git difftool {{base_branch}}...HEAD\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates (cargo clippy + tests) when your process exits and advance the pipeline based on the results.\n\n## Bug Workflow: Root Cause First\nWhen working on bugs:\n1. Investigate the root cause before writing any fix. Use `git bisect` to find the breaking commit or `git log` to trace history. Read the relevant code before touching anything.\n2. Fix the root cause with a surgical, minimal change. Do NOT add new abstractions, wrappers, or workarounds when a targeted fix to the original code is possible.\n3. Write commit messages that explain what broke and why, not just what was changed.\n4. If you cannot determine the root cause after thorough investigation, document what you tried and why it was inconclusive — do not guess and ship a speculative fix." -system_prompt = "You are a senior full-stack engineer working autonomously in a git worktree. You handle complex tasks requiring deep architectural understanding. Follow the Story-Driven Test Workflow strictly. Run cargo clippy --all-targets --all-features and biome checks before considering work complete. Commit all your work before finishing - use a descriptive commit message. Do not accept stories, move them to archived, or merge to master - a human will do that. Do not coordinate with other agents - focus on your assigned story. The server automatically runs acceptance gates when your process exits. For bugs, always find and fix the root cause. Use git bisect to find breaking commits. Do not layer new code on top of existing code when a surgical fix is possible. If root cause is unclear after investigation, document what you tried rather than guessing." - -[[agent]] -name = "qa" -stage = "qa" -role = "Reviews coder work in worktrees: runs quality gates, generates testing plans, and reports findings." -model = "sonnet" -max_turns = 40 -max_budget_usd = 4.00 -prompt = """You are the QA agent for story {{story_id}}. Your job is to review the coder's work in the worktree and produce a structured QA report. - -Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. - -## Your Workflow - -### 1. Code Quality Scan -- Run `git diff master...HEAD --stat` to see what files changed -- Run `git diff master...HEAD` to review the actual changes for obvious coding mistakes (unused imports, dead code, unhandled errors, hardcoded values) -- Run `cargo clippy --all-targets --all-features` and note any warnings -- If a `frontend/` directory exists: - - Run `npm run build` and note any TypeScript errors - - Run `npx @biomejs/biome check src/` and note any linting issues - -### 2. Test Verification -- Run `cargo test` and verify all tests pass -- If `frontend/` exists: run `npm test` and verify all frontend tests pass -- Review test quality: look for tests that are trivial or don't assert meaningful behavior - -### 3. Manual Testing Support -- Build the server: run `cargo build` and note success/failure -- If build succeeds: find a free port (try 3010-3020) and attempt to start the server -- Generate a testing plan including: - - URL to visit in the browser - - Things to check in the UI - - curl commands to exercise relevant API endpoints -- Kill the test server when done: `pkill -f 'target.*storkit' || true` (NEVER use `pkill -f storkit` — it kills the vite dev server) - -### 4. Produce Structured Report -Print your QA report to stdout before your process exits. The server will automatically run acceptance gates. Use this format: - -``` -## QA Report for {{story_id}} - -### Code Quality -- clippy: PASS/FAIL (details) -- TypeScript build: PASS/FAIL/SKIP (details) -- Biome lint: PASS/FAIL/SKIP (details) -- Code review findings: (list any issues found, or "None") - -### Test Verification -- cargo test: PASS/FAIL (N tests) -- npm test: PASS/FAIL/SKIP (N tests) -- Test quality issues: (list any trivial/weak tests, or "None") - -### Manual Testing Plan -- Server URL: http://localhost:PORT (or "Build failed") -- Pages to visit: (list) -- Things to check: (list) -- curl commands: (list) - -### Overall: PASS/FAIL -``` - -## Rules -- Do NOT modify any code — read-only review only -- If the server fails to start, still provide the testing plan with curl commands -- The server automatically runs acceptance gates when your process exits""" -system_prompt = "You are a QA agent. Your job is read-only: review code quality, run tests, try to start the server, and produce a structured QA report. Do not modify code. The server automatically runs acceptance gates when your process exits." - -[[agent]] -name = "mergemaster" -stage = "mergemaster" -role = "Merges completed coder work into master, runs quality gates, archives stories, and cleans up worktrees." -model = "opus" -max_turns = 30 -max_budget_usd = 5.00 -prompt = """You are the mergemaster agent for story {{story_id}}. Your job is to merge the completed coder work into master. - -Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. - -## Your Workflow -1. Call merge_agent_work(story_id='{{story_id}}') via the MCP tool to trigger the full merge pipeline -2. Review the result: check success, had_conflicts, conflicts_resolved, gates_passed, and gate_output -3. If merge succeeded and gates passed: report success to the human -4. If conflicts were auto-resolved (conflicts_resolved=true) and gates passed: report success, noting which conflicts were resolved -5. If conflicts could not be auto-resolved: **resolve them yourself** in the merge worktree (see below) -6. If merge failed for any other reason: call report_merge_failure(story_id='{{story_id}}', reason='
') and report to the human -7. If gates failed after merge: attempt to fix the issues yourself in the merge worktree, then re-trigger merge_agent_work. After 3 fix attempts, call report_merge_failure and stop. - -## Resolving Complex Conflicts Yourself - -When the auto-resolver fails, you have access to the merge worktree at `.story_kit/merge_workspace/`. Go in there and resolve the conflicts manually: - -1. Run `git diff --name-only --diff-filter=U` in the merge worktree to list conflicted files -2. **Build context before touching code.** Run `git log --oneline master...HEAD` on the feature branch to see its commits. Then run `git log --oneline --since="$(git log -1 --format=%ci )" master` to see what landed on master since the branch was created. Read the story files in `.story_kit/work/` for any recently merged stories that touch the same files — this tells you WHY master changed and what must be preserved. -3. Read each conflicted file and understand both sides of the conflict -4. **Understand intent, not just syntax.** The feature branch may be behind master — master's version of shared infrastructure is almost always correct. The feature branch's contribution is the NEW functionality it adds. Your job is to integrate the new into master's structure, not pick one side. -5. Resolve by integrating the feature's new functionality into master's code structure -5. Stage resolved files with `git add` -6. Run `cargo check` (and `npm run build` if frontend changed) to verify compilation -7. If it compiles, commit and re-trigger merge_agent_work - -### Common conflict patterns in this project: - -**Story file rename/rename conflicts:** Both branches moved the story .md file to different pipeline directories. Resolution: `git rm` both sides — story files in `work/2_current/`, `work/3_qa/`, `work/4_merge/` are gitignored and don't need to be committed. - -**bot.rs tokio::select! conflicts:** Master has a `tokio::select!` loop in `handle_message()` that handles permission forwarding (story 275). Feature branches created before story 275 have a simpler direct `provider.chat_stream().await` call. Resolution: KEEP master's tokio::select! loop. Integrate only the feature's new logic (e.g. typing indicators, new callbacks) into the existing loop structure. Do NOT replace the loop with the old direct call. - -**Duplicate functions/imports:** The auto-resolver keeps both sides, producing duplicates. Resolution: keep one copy (prefer master's version), delete the duplicate. - -**Formatting-only conflicts:** Both sides reformatted the same code differently. Resolution: pick either side (prefer master). - -## Fixing Gate Failures - -If quality gates fail (cargo clippy, cargo test, npm run build, npm test), attempt to fix issues yourself in the merge worktree. - -**Fix yourself (up to 3 attempts total):** -- Syntax errors (missing semicolons, brackets, commas) -- Duplicate definitions from merge artifacts -- Simple type annotation errors -- Unused import warnings flagged by clippy -- Mismatched braces from bad conflict resolution -- Trivial formatting issues that block compilation or linting - -**Report to human without attempting a fix:** -- Logic errors or incorrect business logic -- Missing function implementations -- Architectural changes required -- Non-trivial refactoring needed - -**Max retry limit:** If gates still fail after 3 fix attempts, call report_merge_failure to record the failure, then stop immediately and report the full gate output to the human. - -## CRITICAL Rules -- NEVER manually move story files between pipeline stages (e.g. from 4_merge/ to 5_done/) -- NEVER call accept_story — only merge_agent_work can move stories to done after a successful merge -- When merge fails after exhausting your fix attempts, ALWAYS call report_merge_failure -- Report conflict resolution outcomes clearly -- Report gate failures with full output so the human can act if needed -- The server automatically runs acceptance gates when your process exits""" -system_prompt = "You are the mergemaster agent. Your primary job is to merge feature branches to master. First try the merge_agent_work MCP tool. If the auto-resolver fails on complex conflicts, resolve them yourself in the merge worktree — you are an opus-class agent capable of understanding both sides of a conflict and producing correct merged code. Common patterns: keep master's tokio::select! permission loop in bot.rs, discard story file rename conflicts (gitignored), remove duplicate definitions. After resolving, verify compilation before re-triggering merge. CRITICAL: Never manually move story files or call accept_story. After 3 failed fix attempts, call report_merge_failure and stop." diff --git a/.storkit/specs/00_CONTEXT.md b/.storkit/specs/00_CONTEXT.md deleted file mode 100644 index 92ce75f..0000000 --- a/.storkit/specs/00_CONTEXT.md +++ /dev/null @@ -1,33 +0,0 @@ -# Project Context - -## High-Level Goal -To build a standalone **Agentic AI Code Assistant** application as a single Rust binary that serves a Vite/React web UI and exposes a WebSocket API. The assistant will facilitate a test-driven development (TDD) workflow first, with both unit and integration tests providing the primary guardrails for code changes. Once the single-threaded TDD workflow is stable and usable (including compatibility with lower-cost agents), the project will evolve to a multi-agent orchestration model using Git worktrees and supervisory roles to maximize throughput. Unlike a passive chat interface, this assistant acts as an **Agent**, capable of using tools to read the filesystem, execute shell commands, manage git repositories, and modify code directly to implement features. - -## Core Features -1. **Chat Interface:** A conversational UI for the user to interact with the AI assistant. -2. **Agentic Tool Bridge:** A robust system mapping LLM "Tool Calls" to native Rust functions. - * **Filesystem:** Read/Write access (scoped to the target project). - * **Search:** High-performance file searching (ripgrep-style) and content retrieval. - * **Shell Integration:** Ability to execute approved commands (e.g., `cargo`, `npm`, `git`) to run tests, linters, and version control. -3. **Workflow Management:** Specialized tools to manage a TDD-first lifecycle: - * Defining test requirements (unit + integration) before code changes. - * Implementing code via red-green-refactor. - * Enforcing test and quality gates before acceptance. - * Scaling later to multi-agent orchestration with Git worktrees and supervisory checks, after the single-threaded process is stable. -4. **LLM Integration:** Connection to an LLM backend to drive the intelligence and tool selection. - * **Remote:** Support for major APIs (Anthropic Claude, Google Gemini, OpenAI, etc). - * **Local:** Support for local inference via Ollama. - -## Domain Definition -* **User:** A software engineer using the assistant to build a project. -* **Target Project:** The local software project the user is working on. -* **Agent:** The AI entity that receives prompts and decides which **Tools** to invoke to solve the problem. -* **Tool:** A discrete function exposed to the Agent (e.g., `run_shell_command`, `write_file`, `search_project`). -* **Story:** A unit of work defining a change (Feature Request). -* **Spec:** A persistent documentation artifact defining the current truth of the system. - -## Glossary -* **SDSW:** Story-Driven Spec Workflow. -* **Web Server Binary:** The Rust binary that serves the Vite/React frontend and exposes the WebSocket API. -* **Living Spec:** The collection of Markdown files in `.story_kit/` that define the project. -* **Tool Call:** A structured request from the LLM to execute a specific native function. diff --git a/.storkit/specs/functional/SLACK_SETUP.md b/.storkit/specs/functional/SLACK_SETUP.md deleted file mode 100644 index 062709e..0000000 --- a/.storkit/specs/functional/SLACK_SETUP.md +++ /dev/null @@ -1,44 +0,0 @@ -# Slack Integration Setup - -## Bot Configuration - -Slack integration is configured via `bot.toml` in the project's `.story_kit/` directory: - -```toml -transport = "slack" -display_name = "Storkit" -slack_bot_token = "xoxb-..." -slack_signing_secret = "..." -slack_channel_ids = ["C01ABCDEF"] -``` - -## Slack App Configuration - -### Event Subscriptions - -1. In your Slack app settings, enable **Event Subscriptions**. -2. Set the **Request URL** to: `https:///webhook/slack` -3. Subscribe to the `message.channels` and `message.im` bot events. - -### Slash Commands - -Slash commands provide quick access to pipeline commands without mentioning the bot. - -1. In your Slack app settings, go to **Slash Commands**. -2. Create the following commands, all pointing to the same **Request URL**: `https:///webhook/slack/command` - -| Command | Description | -|---------|-------------| -| `/storkit-status` | Show pipeline status and agent availability | -| `/storkit-cost` | Show token spend: 24h total, top stories, and breakdown | -| `/storkit-show` | Display the full text of a work item (e.g. `/storkit-show 42`) | -| `/storkit-git` | Show git status: branch, changes, ahead/behind | -| `/storkit-htop` | Show system and agent process dashboard | - -All slash command responses are **ephemeral** — only the user who invoked the command sees the response. - -### OAuth & Permissions - -Required bot token scopes: -- `chat:write` — send messages -- `commands` — handle slash commands diff --git a/.storkit/specs/functional/UI_LAYOUT.md b/.storkit/specs/functional/UI_LAYOUT.md deleted file mode 100644 index 9993294..0000000 --- a/.storkit/specs/functional/UI_LAYOUT.md +++ /dev/null @@ -1,33 +0,0 @@ -# Functional Spec: UI Layout - -## 1. Global Structure -The application uses a **fixed-layout** strategy to maximize chat visibility. - -```text -+-------------------------------------------------------+ -| HEADER (Fixed Height, e.g., 50px) | -| [Project: ~/foo/bar] [Model: llama3] [x] Tools | -+-------------------------------------------------------+ -| | -| CHAT AREA (Flex Grow, Scrollable) | -| | -| (User Message) | -| (Agent Message) | -| | -+-------------------------------------------------------+ -| INPUT AREA (Fixed Height, Bottom) | -| [ Input Field ........................... ] [Send] | -+-------------------------------------------------------+ -``` - -## 2. Components -* **Header:** Contains global context (Project) and session config (Model/Tools). - * *Constraint:* Must not scroll away. -* **ChatList:** The scrollable container for messages. -* **InputBar:** Pinned to the bottom. - -## 3. Styling -* Use Flexbox (`flex-direction: column`) on the main container. -* Header: `flex-shrink: 0`. -* ChatList: `flex-grow: 1`, `overflow-y: auto`. -* InputBar: `flex-shrink: 0`. diff --git a/.storkit/specs/functional/UI_UX.md b/.storkit/specs/functional/UI_UX.md deleted file mode 100644 index 6171a14..0000000 --- a/.storkit/specs/functional/UI_UX.md +++ /dev/null @@ -1,474 +0,0 @@ -# Functional Spec: UI/UX Responsiveness - -## Problem -Currently, the `chat` command in Rust is an async function that performs a long-running, blocking loop (waiting for LLM, executing tools). While Tauri executes this on a separate thread from the UI, the frontend awaits the *entire* result before re-rendering. This makes the app feel "frozen" because there is no feedback during the 10-60 seconds of generation. - -## Solution: Event-Driven Feedback -Instead of waiting for the final array of messages, the Backend should emit **Events** to the Frontend in real-time. - -### 1. Events -* `chat:token`: Emitted when a text token is generated (Streaming text). -* `chat:tool-start`: Emitted when a tool call begins (e.g., `{ tool: "git status" }`). -* `chat:tool-end`: Emitted when a tool call finishes (e.g., `{ output: "..." }`). - -### 2. Implementation Strategy - -#### Token-by-Token Streaming (Story 18) -The system now implements full token streaming for real-time response display: - -* **Backend (Rust):** - * Set `stream: true` in Ollama API requests - * Parse newline-delimited JSON from Ollama's streaming response - * Emit `chat:token` events for each token received - * Use `reqwest` streaming body with async iteration - * After streaming completes, emit `chat:update` with the full message - -* **Frontend (TypeScript):** - * Listen for `chat:token` events - * Append tokens to the current assistant message in real-time - * Maintain smooth auto-scroll as tokens arrive - * After streaming completes, process `chat:update` for final state - -* **Event-Driven Updates:** - * `chat:token`: Emitted for each token during streaming (payload: `{ content: string }`) - * `chat:update`: Emitted after LLM response complete or after Tool Execution (payload: `Message[]`) - * Frontend maintains streaming state separate from message history - -### 3. Visuals -* **Loading State:** The "Send" button should show a spinner or "Stop" button. -* **Auto-Scroll:** The chat view uses smart auto-scroll that respects user scrolling (see Smart Auto-Scroll section below). - -## Smart Auto-Scroll (Story 22) - -### Problem -Users need to review previous messages while the AI is streaming new content, but aggressive auto-scrolling constantly drags them back to the bottom, making it impossible to read older content. - -### Solution: Scroll-Position-Aware Auto-Scroll - -The chat implements intelligent auto-scroll that: -* Automatically scrolls to show new content when the user is at/near the bottom -* Pauses auto-scroll when the user scrolls up to review older messages -* Resumes auto-scroll when the user scrolls back to the bottom - -### Requirements - -1. **Scroll Detection:** Track whether the user is at the bottom of the chat -2. **Threshold:** Define "near bottom" as within 25px of the bottom -3. **Auto-Scroll Logic:** Only trigger auto-scroll if user is at/near bottom -4. **Smooth Operation:** No flickering or jarring behavior during scrolling -5. **Universal:** Works during both streaming responses and tool execution - -### Implementation Notes - -**Core Components:** -* `scrollContainerRef`: Reference to the scrollable messages container -* `shouldAutoScrollRef`: Tracks whether auto-scroll should be active (uses ref to avoid re-renders) -* `messagesEndRef`: Target element for scroll-to-bottom behavior - -**Detection Function:** -```typescript -const isScrolledToBottom = () => { - const element = scrollContainerRef.current; - if (!element) return true; - const threshold = 25; // pixels from bottom - return ( - element.scrollHeight - element.scrollTop - element.clientHeight < threshold - ); -}; -``` - -**Scroll Handler:** -```typescript -const handleScroll = () => { - // Update auto-scroll state based on scroll position - shouldAutoScrollRef.current = isScrolledToBottom(); -}; -``` - -**Conditional Auto-Scroll:** -```typescript -useEffect(() => { - if (shouldAutoScrollRef.current) { - scrollToBottom(); - } -}, [messages, streamingContent]); -``` - -**DOM Setup:** -* Attach `ref={scrollContainerRef}` to the messages container -* Attach `onScroll={handleScroll}` to detect user scrolling -* Initialize `shouldAutoScrollRef` to `true` (enable auto-scroll by default) - -### Edge Cases - -1. **Initial Load:** Auto-scroll is enabled by default -2. **Rapid Scrolling:** Uses refs to avoid race conditions and excessive re-renders -3. **Manual Scroll to Bottom:** Auto-scroll re-enables when user scrolls near bottom -4. **No Container:** Falls back to always allowing auto-scroll if container ref is null - -## Tool Output Display - -### Problem -Tool outputs (like file contents, search results, or command output) can be very long, making the chat history difficult to read. Users need to see the Agent's reasoning and responses without being overwhelmed by verbose tool output. - -### Solution: Collapsible Tool Outputs -Tool outputs should be rendered in a collapsible component that is **closed by default**. - -### Requirements - -1. **Default State:** Tool outputs are collapsed/closed when first rendered -2. **Summary Line:** Shows essential information without expanding: - - Tool name (e.g., `read_file`, `exec_shell`) - - Key arguments (e.g., file path, command name) - - Format: "▶ tool_name(key_arg)" - - Example: "▶ read_file(src/main.rs)" - - Example: "▶ exec_shell(cargo check)" -3. **Expandable:** User can click the summary to toggle expansion -4. **Output Display:** When expanded, shows the complete tool output in a readable format: - - Use `
` or monospace font for code/terminal output
-   - Preserve whitespace and line breaks
-   - Limit height with scrolling for very long outputs (e.g., max-height: 300px)
-5. **Visual Indicator:** Clear arrow or icon showing collapsed/expanded state
-6. **Styling:** Consistent with the dark theme, distinguishable from assistant messages
-
-### Implementation Notes
-*   Use native `
` and `` HTML elements for accessibility -* Or implement custom collapsible component with proper ARIA attributes -* Tool outputs should be visually distinct (border, background color, or badge) -* Multiple tool calls in sequence should each be independently collapsible - -## Scroll Bar Styling - -### Problem -Visible scroll bars create visual clutter and make the interface feel less polished. Standard browser scroll bars can be distracting and break the clean aesthetic of the dark theme. - -### Solution: Hidden Scroll Bars with Maintained Functionality -Scroll bars should be hidden while maintaining full scroll functionality. - -### Requirements - -1. **Visual:** Scroll bars should not be visible to the user -2. **Functionality:** Scrolling must still work perfectly: - - Mouse wheel scrolling - - Trackpad scrolling - - Keyboard navigation (arrow keys, page up/down) - - Auto-scroll to bottom for new messages -3. **Cross-browser:** Solution must work on Chrome, Firefox, and Safari -4. **Areas affected:** - - Main chat message area (vertical scroll) - - Tool output content (both vertical and horizontal) - - Any other scrollable containers - -### Implementation Notes -* Use CSS `scrollbar-width: none` for Firefox -* Use `::-webkit-scrollbar { display: none; }` for Chrome/Safari/Edge -* Maintain `overflow: auto` or `overflow-y: scroll` to preserve scroll functionality -* Ensure `overflow-x: hidden` where horizontal scroll is not needed -* Test with very long messages and large tool outputs to ensure no layout breaking - -## Text Alignment and Readability - -### Problem -Center-aligned text in a chat interface is unconventional and reduces readability, especially for code blocks and long-form content. Standard chat UIs align messages differently based on the sender. - -### Solution: Context-Appropriate Text Alignment -Messages should follow standard chat UI conventions with proper alignment based on message type. - -### Requirements - -1. **User Messages:** Right-aligned (standard pattern showing messages sent by the user) -2. **Assistant Messages:** Left-aligned (standard pattern showing messages received) -3. **Tool Outputs:** Left-aligned (part of the system/assistant response flow) -4. **Code Blocks:** Always left-aligned regardless of message type (for readability) -5. **Container:** Remove any center-alignment from the chat container -6. **Max-Width:** Maintain current max-width constraint (e.g., 768px) for optimal readability -7. **Spacing:** Maintain proper padding and visual hierarchy between messages - -### Implementation Notes -* Check for `textAlign: "center"` in inline styles and remove -* Check for `text-align: center` in CSS and remove from chat-related classes -* Ensure flexbox alignment is set appropriately: - * User messages: `alignItems: "flex-end"` - * Assistant/Tool messages: `alignItems: "flex-start"` -* Code blocks should have `text-align: left` explicitly set - -## Syntax Highlighting - -### Problem -Code blocks in assistant responses currently lack syntax highlighting, making them harder to read and understand. Developers expect colored syntax highlighting similar to their code editors. - -### Solution: Syntax Highlighting for Code Blocks -Integrate syntax highlighting into markdown code blocks rendered by the assistant. - -### Requirements - -1. **Languages Supported:** At minimum: - - JavaScript/TypeScript - - Rust - - Python - - JSON - - Markdown - - Shell/Bash - - HTML/CSS - - SQL -2. **Theme:** Use a dark theme that complements the existing dark UI (e.g., `oneDark`, `vsDark`, `dracula`) -3. **Integration:** Work seamlessly with `react-markdown` component -4. **Performance:** Should not significantly impact rendering performance -5. **Fallback:** Plain monospace text for unrecognized languages -6. **Inline Code:** Inline code (single backticks) should maintain simple styling without full syntax highlighting - -### Implementation Notes -* Use `react-syntax-highlighter` library with `react-markdown` -* Or use `rehype-highlight` plugin for `react-markdown` -* Configure with a dark theme preset (e.g., `oneDark` from `react-syntax-highlighter/dist/esm/styles/prism`) -* Apply to code blocks via `react-markdown` components prop: - ```tsx - { - const match = /language-(\w+)/.exec(className || ''); - return !inline && match ? ( - - {String(children).replace(/\n$/, '')} - - ) : ( - {children} - ); - } - }} - /> - ``` -* Ensure syntax highlighted code blocks are left-aligned -* Test with various code samples to ensure proper rendering - -## Token Streaming - -### Problem -Without streaming, users see no feedback during model generation. The response appears all at once after waiting, which feels unresponsive and provides no indication that the system is working. - -### Solution: Token-by-Token Streaming -Stream tokens from Ollama in real-time and display them as they arrive, providing immediate feedback and a responsive chat experience similar to ChatGPT. - -### Requirements - -1. **Real-time Display:** Tokens appear immediately as Ollama generates them -2. **Smooth Performance:** No lag or stuttering during high token throughput -3. **Tool Compatibility:** Streaming works correctly with tool calls and multi-turn conversations -4. **Auto-scroll:** Chat view follows streaming content automatically -5. **Error Handling:** Gracefully handle stream interruptions or errors -6. **State Management:** Maintain clean separation between streaming state and final message history - -### Implementation Notes - -#### Backend (Rust) -* Enable streaming in Ollama requests: `stream: true` -* Parse newline-delimited JSON from response body -* Each line is a separate JSON object: `{"message":{"content":"token"},"done":false}` -* Use `futures::StreamExt` or similar for async stream processing -* Emit `chat:token` event for each token -* Emit `chat:update` when streaming completes -* Handle both streaming text and tool call interruptions - -#### Frontend (TypeScript) -* Create streaming state separate from message history -* Listen for `chat:token` events and append to streaming buffer -* Render streaming content in real-time -* On `chat:update`, replace streaming content with final message -* Maintain scroll position during streaming - -#### Ollama Streaming Format -```json -{"message":{"role":"assistant","content":"Hello"},"done":false} -{"message":{"role":"assistant","content":" world"},"done":false} -{"message":{"role":"assistant","content":"!"},"done":true} -{"message":{"role":"assistant","tool_calls":[...]},"done":true} -``` - -### Edge Cases -* Tool calls during streaming: Switch from text streaming to tool execution -* Cancellation during streaming: Clean up streaming state properly -* Network interruptions: Show error and preserve partial content -* Very fast streaming: Throttle UI updates if needed for performance - -## Input Focus Management - -### Problem -When the app loads with a project selected, users need to click into the chat input box before they can start typing. This adds unnecessary friction to the user experience. - -### Solution: Auto-focus on Component Mount -The chat input field should automatically receive focus when the chat component mounts, allowing users to immediately start typing. - -### Requirements - -1. **Auto-focus:** Input field receives focus automatically when chat component loads -2. **Visible Cursor:** Cursor should be visible and blinking in the input field -3. **Immediate Typing:** User can start typing without clicking into the field -4. **Non-intrusive:** Should not interfere with other UI interactions or accessibility -5. **Timing:** Focus should be set after the component fully mounts - -### Implementation Notes -* Use React `useRef` to create a reference to the input element -* Use `useEffect` with empty dependency array to run once on mount -* Call `inputRef.current?.focus()` in the effect -* Ensure the ref is properly attached to the input element -* Example implementation: - ```tsx - const inputRef = useRef(null); - - useEffect(() => { - inputRef.current?.focus(); - }, []); - - return - ``` - -## Response Interruption - -### Problem -Users may want to interrupt a long-running model response to ask a different question or change direction. Having to wait for the full response to complete creates friction and wastes time. - -### Solution: Interrupt on Typing -When the user starts typing in the input field while the model is generating a response, the generation should be cancelled immediately, allowing the user to send a new message. - -### Requirements - -1. **Input Always Enabled:** The input field should remain enabled and usable even while the model is generating -2. **Interrupt Detection:** Detect when user types in the input field while `loading` state is true -3. **Immediate Cancellation:** Cancel the ongoing generation as soon as typing is detected -4. **Preserve Partial Response:** Any partial response generated before interruption should remain visible in the chat -5. **State Reset:** UI should return to normal state (ready to send) after interruption -6. **Preserve User Input:** The user's new input should be preserved in the input field -7. **Visual Feedback:** "Thinking..." indicator should disappear when generation is interrupted - -### Implementation Notes -* Do NOT disable the input field during loading -* Listen for input changes while `loading` is true -* When user types during loading, call backend to cancel generation (if possible) or just stop waiting -* Set `loading` state to false immediately when typing detected -* Backend may need a `cancel_chat` command or similar -* Consider if Ollama requests can be cancelled mid-generation or if we just stop processing the response -* Example implementation: - ```tsx - const handleInputChange = (e: React.ChangeEvent) => { - const newValue = e.target.value; - setInput(newValue); - - // If user starts typing while model is generating, interrupt - if (loading && newValue.length > input.length) { - setLoading(false); - // Optionally call backend to cancel: invoke("cancel_chat") - } - }; - ``` - -## Session Management - -### Problem -Users may want to start a fresh conversation without restarting the application. Long conversations can become unwieldy, and users need a way to clear context for new tasks while keeping the same project open. - -### Solution: New Session Button -Provide a clear, accessible way for users to start a new session by clearing the chat history. - -### Requirements - -1. **Button Placement:** Located in the header area, near model controls -2. **Visual Design:** Secondary/subtle styling to prevent accidental clicks -3. **Confirmation Dialog:** Ask "Are you sure? This will clear all messages." before clearing -4. **State Management:** - - Clear `messages` state array - - Clear `streamingContent` if any streaming is in progress - - Preserve project path, model selection, and tool settings - - Cancel any in-flight backend operations before clearing -5. **User Feedback:** Immediate visual response (messages disappear) -6. **Empty State:** Show a welcome message or empty state after clearing - -### Implementation Notes - -**Frontend:** -- Add "New Session" button to header -- Implement confirmation modal/dialog -- Call `setMessages([])` after confirmation -- Cancel any ongoing streaming/tool execution -- Consider keyboard shortcut (e.g., Cmd/Ctrl+K) - -**Backend:** -- May need to cancel ongoing chat operations -- Clear any server-side state if applicable -- No persistent session history (sessions are ephemeral) - -**Edge Cases:** -- Don't clear while actively streaming (cancel first, then clear) -- Handle confirmation dismissal (do nothing) -- Ensure button is always accessible (not disabled) - -### Button Label Options -- "New Session" (clear and descriptive) -- "Clear Chat" (direct but less friendly) -- "Start Over" (conversational) -- Icon: 🔄 or ⊕ (plus in circle) - -## Context Window Usage Display - -### Problem -Users have no visibility into how much of the model's context window they're using. This leads to: -- Unexpected quality degradation when context limit is reached -- Uncertainty about when to start a new session -- Inability to gauge conversation length - -### Solution: Real-time Context Usage Indicator -Display a persistent indicator showing current token usage vs. model's context window limit. - -### Requirements - -1. **Visual Indicator:** Always visible in header area -2. **Real-time Updates:** Updates as messages are added -3. **Model-Aware:** Shows correct limit based on selected model -4. **Color Coding:** Visual warning as limit approaches - - Green/default: 0-74% usage - - Yellow/warning: 75-89% usage - - Red/danger: 90-100% usage -5. **Clear Format:** "2.5K / 8K tokens (31%)" or similar -6. **Token Estimation:** Approximate token count for all messages - -### Implementation Notes - -**Token Estimation:** -- Use simple approximation: 1 token ≈ 4 characters -- Or integrate `gpt-tokenizer` for more accuracy -- Count: system prompts + user messages + assistant responses + tool outputs + tool calls - -**Model Context Windows:** -- llama3.1, llama3.2: 8K tokens -- qwen2.5-coder: 32K tokens -- deepseek-coder: 16K tokens -- Default/unknown: 8K tokens - -**Calculation:** -```tsx -const estimateTokens = (text: string): number => { - return Math.ceil(text.length / 4); -}; - -const calculateContextUsage = (messages: Message[], systemPrompt: string) => { - let total = estimateTokens(systemPrompt); - messages.forEach(msg => { - total += estimateTokens(msg.content); - if (msg.tool_calls) { - total += estimateTokens(JSON.stringify(msg.tool_calls)); - } - }); - return total; -}; -``` - -**UI Placement:** -- Header area, near model selector -- Non-intrusive but always visible -- Optional tooltip with breakdown on hover - -### Edge Cases -- Empty conversation: Show "0 / 8K" -- During streaming: Include partial content -- After clearing: Reset to 0 -- Model change: Update context window limit - diff --git a/.storkit/specs/tech/STACK.md b/.storkit/specs/tech/STACK.md deleted file mode 100644 index 5ec7ede..0000000 --- a/.storkit/specs/tech/STACK.md +++ /dev/null @@ -1,130 +0,0 @@ -# Tech Stack & Constraints - -## Overview -This project is a standalone Rust **web server binary** that serves a Vite/React frontend and exposes a **WebSocket API**. The built frontend assets are packaged with the binary (in a `frontend` directory) and served as static files. It functions as an **Agentic Code Assistant** capable of safely executing tools on the host system. - -## Core Stack -* **Backend:** Rust (Web Server) - * **MSRV:** Stable (latest) - * **Framework:** Poem HTTP server with WebSocket support for streaming; HTTP APIs should use Poem OpenAPI (Swagger) for non-streaming endpoints. -* **Frontend:** TypeScript + React - * **Build Tool:** Vite - * **Package Manager:** npm - * **Styling:** CSS Modules or Tailwind (TBD - Defaulting to CSS Modules) - * **State Management:** React Context / Hooks - * **Chat UI:** Rendered Markdown with syntax highlighting. - -## Agent Architecture -The application follows a **Tool-Use (Function Calling)** architecture: -1. **Frontend:** Collects user input and sends it to the LLM. -2. **LLM:** Decides to generate text OR request a **Tool Call** (e.g., `execute_shell`, `read_file`). -3. **Web Server Backend (The "Hand"):** - * Intercepts Tool Calls. - * Validates the request against the **Safety Policy**. - * Executes the native code (File I/O, Shell Process, Search). - * Returns the output (stdout/stderr/file content) to the LLM. - * **Streaming:** The backend sends real-time updates over WebSocket to keep the UI responsive during long-running Agent tasks. - -## LLM Provider Abstraction -To support both Remote and Local models, the system implements a `ModelProvider` abstraction layer. - -* **Strategy:** - * Abstract the differences between API formats (OpenAI-compatible vs Anthropic vs Gemini). - * Normalize "Tool Use" definitions, as each provider handles function calling schemas differently. -* **Supported Providers:** - * **Ollama:** Local inference (e.g., Llama 3, DeepSeek Coder) for privacy and offline usage. - * **Anthropic:** Claude 3.5 models (Sonnet, Haiku) via API for coding tasks (Story 12). -* **Provider Selection:** - * Automatic detection based on model name prefix: - * `claude-` → Anthropic API - * Otherwise → Ollama - * Single unified model dropdown with section headers ("Anthropic", "Ollama") -* **API Key Management:** - * Anthropic API key stored server-side and persisted securely - * On first use of Claude model, user prompted to enter API key - * Key persists across sessions (no re-entry needed) - -## Tooling Capabilities - -### 1. Filesystem (Native) -* **Scope:** Strictly limited to the user-selected `project_root`. -* **Operations:** Read, Write, List, Delete. -* **Constraint:** Modifications to `.git/` are strictly forbidden via file APIs (use Git tools instead). - -### 2. Shell Execution -* **Library:** `tokio::process` for async execution. -* **Constraint:** We do **not** run an interactive shell (repl). We run discrete, stateless commands. -* **Allowlist:** The agent may only execute specific binaries: - * `git` - * `cargo`, `rustc`, `rustfmt`, `clippy` - * `npm`, `node`, `yarn`, `pnpm`, `bun` - * `ls`, `find`, `grep` (if not using internal search) - * `mkdir`, `rm`, `touch`, `mv`, `cp` - -### 3. Search & Navigation -* **Library:** `ignore` (by BurntSushi) + `grep` logic. -* **Behavior:** - * Must respect `.gitignore` files automatically. - * Must be performant (parallel traversal). - -## Coding Standards - -### Rust -* **Style:** `rustfmt` standard. -* **Linter:** `clippy` - Must pass with 0 warnings before merging. -* **Error Handling:** Custom `AppError` type deriving `thiserror`. All Commands return `Result`. -* **Concurrency:** Heavy tools (Search, Shell) must run on `tokio` threads to avoid blocking the UI. -* **Quality Gates:** - * `cargo clippy --all-targets --all-features` must show 0 errors, 0 warnings - * `cargo check` must succeed - * `cargo nextest run` must pass all tests -* **Test Coverage:** - * Generate JSON report: `cargo llvm-cov nextest --no-clean --json --output-path .story_kit/coverage/server.json` - * Generate lcov report: `cargo llvm-cov report --lcov --output-path .story_kit/coverage/server.lcov` - * Reports are written to `.story_kit/coverage/` (excluded from git) - -### TypeScript / React -* **Style:** Biome formatter (replaces Prettier/ESLint). -* **Linter:** Biome - Must pass with 0 errors, 0 warnings before merging. -* **Types:** Shared types with Rust (via `tauri-specta` or manual interface matching) are preferred to ensure type safety across the bridge. -* **Testing:** Vitest for unit/component tests; Playwright for end-to-end tests. -* **Quality Gates:** - * `npx @biomejs/biome check src/` must show 0 errors, 0 warnings - * `npm run build` must succeed - * `npm test` must pass - * `npm run test:e2e` must pass - * No `any` types allowed (use proper types or `unknown`) - * React keys must use stable IDs, not array indices - * All buttons must have explicit `type` attribute - -## Libraries (Approved) -* **Rust:** - * `serde`, `serde_json`: Serialization. - * `ignore`: Fast recursive directory iteration respecting gitignore. - * `walkdir`: Simple directory traversal. - * `tokio`: Async runtime. - * `reqwest`: For LLM API calls (Anthropic, Ollama). - * `eventsource-stream`: For Server-Sent Events (Anthropic streaming). - * `uuid`: For unique message IDs. - * `chrono`: For timestamps. - * `poem`: HTTP server framework. - * `poem-openapi`: OpenAPI (Swagger) for non-streaming HTTP APIs. -* **JavaScript:** - * `react-markdown`: For rendering chat responses. - * `vitest`: Unit/component testing. - * `playwright`: End-to-end testing. - -## Running the App (Worktrees & Ports) - -Multiple instances can run simultaneously in different worktrees. To avoid port conflicts: - -- **Backend:** Set `STORKIT_PORT` to a unique port (default is 3001). Example: `STORKIT_PORT=3002 cargo run` -- **Frontend:** Run `npm run dev` from `frontend/`. It auto-selects the next unused port. It reads `STORKIT_PORT` to know which backend to talk to, so export it before running: `export STORKIT_PORT=3002 && cd frontend && npm run dev` - -When running in a worktree, use a port that won't conflict with the main instance (3001). Ports 3002+ are good choices. - -## Safety & Sandbox -1. **Project Scope:** The application must strictly enforce that it does not read/write outside the `project_root` selected by the user. -2. **Human in the Loop:** - * Shell commands that modify state (non-readonly) should ideally require a UI confirmation (configurable). - * File writes must be confirmed or revertible. diff --git a/.story_kit_port b/.story_kit_port deleted file mode 100644 index 6fc99cf..0000000 --- a/.story_kit_port +++ /dev/null @@ -1 +0,0 @@ -3001 \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index e6ccd08..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,3 +0,0 @@ -Never chain shell commands with `&&`, `||`, or `;` in a single Bash call. The permission system validates the entire command string, and chained commands won't match allow rules like `Bash(git *)`. Use separate Bash calls instead — parallel calls work fine. - -Read .storkit/README.md to see our dev process. diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index b6f32a6..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,5694 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "accessory" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e416a3ab45838bac2ab2d81b1088d738d7b2d2c5272a54d39366565a29bd80" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - -[[package]] -name = "aquamarine" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" -dependencies = [ - "include_dir", - "itertools 0.10.5", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "archery" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -dependencies = [ - "serde", -] - -[[package]] -name = "as_variant" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbc3a507a82b17ba0d98f6ce8fd6954ea0c8152e98009d36a40d8dcc8ce078a" - -[[package]] -name = "assert-json-diff" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "assign" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" -dependencies = [ - "compression-codecs", - "compression-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "aws-lc-rs" -version = "1.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "backon" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" -dependencies = [ - "fastrand", - "gloo-timers", - "tokio", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -dependencies = [ - "serde_core", -] - -[[package]] -name = "bitmaps" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" - -[[package]] -name = "blake3" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "cpufeatures", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "bstr" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "bytesize" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - -[[package]] -name = "cc" -version = "1.2.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", - "zeroize", -] - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link 0.2.1", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - -[[package]] -name = "cmake" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] - -[[package]] -name = "colored" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "compression-codecs" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" -dependencies = [ - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_panic" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" -dependencies = [ - "typewit", -] - -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "serde", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "date_header" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c03c416ed1a30fbb027ef484ba6ab6f80e1eada675e1a2b92fd673c045a1f1d" - -[[package]] -name = "deadpool" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" -dependencies = [ - "deadpool-runtime", - "lazy_static", - "num_cpus", - "tokio", -] - -[[package]] -name = "deadpool-runtime" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" -dependencies = [ - "tokio", -] - -[[package]] -name = "deadpool-sync" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524bc3df0d57e98ecd022e21ba31166c2625e7d3e5bcc4510efaeeab4abcab04" -dependencies = [ - "deadpool-runtime", -] - -[[package]] -name = "decancer" -version = "3.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9244323129647178bf41ac861a2cdb9d9c81b9b09d3d0d1de9cd302b33b8a1d" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "delegate-display" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9926686c832494164c33a36bf65118f4bd6e704000b58c94681bf62e9ad67a74" -dependencies = [ - "impartial-ord", - "itoa", - "macroific", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl 1.0.0", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl 2.1.1", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "serde", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core 0.6.4", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "eventsource-stream" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" -dependencies = [ - "futures-core", - "nom", - "pin-project-lite", -] - -[[package]] -name = "eyeball" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93bd0ebf93d61d6332d3c09a96e97975968a44e19a64c947bde06e6baff383f" -dependencies = [ - "futures-core", - "readlock", - "readlock-tokio", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "eyeball-im" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4790c03df183c2b46665c1a58118c04fd3e3976ec2fe16a0aa00e00c9eea7754" -dependencies = [ - "futures-core", - "imbl", - "tokio", - "tracing", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fancy_constructor" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a27643a5d05f3a22f5afd6e0d0e6e354f92d37907006f97b84b9cb79082198" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "filedescriptor" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" -dependencies = [ - "libc", - "thiserror 1.0.69", - "winapi", -] - -[[package]] -name = "filetime" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" -dependencies = [ - "cfg-if", - "libc", - "libredox", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "futures_codec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad004dd81205978a2bba6c566ed70535ccf88c0be34649e628186474603f43ca" -dependencies = [ - "bytes", - "futures-sink", - "futures-util", - "memchr", - "pin-project-lite", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "wasip2", - "wasip3", -] - -[[package]] -name = "globset" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "growable-bloom-filter" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" -dependencies = [ - "serde", - "serde_bytes", - "serde_derive", - "xxhash-rust", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "hashlink" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "homedir" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68df315d2857b2d8d2898be54a85e1d001bbbe0dbb5f8ef847b48dd3a23c4527" -dependencies = [ - "cfg-if", - "nix 0.30.1", - "widestring", - "windows", -] - -[[package]] -name = "html5ever" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4" -dependencies = [ - "log", - "markup5ever", - "match_token", -] - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-auth" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "150fa4a9462ef926824cf4519c84ed652ca8f4fbae34cb8af045b5cbcaf98822" -dependencies = [ - "memchr", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core 0.62.2", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "ignore" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "imbl" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fade8ae6828627ad1fa094a891eccfb25150b383047190a3648d66d06186501" -dependencies = [ - "archery", - "bitmaps", - "imbl-sized-chunks", - "rand_core 0.9.5", - "rand_xoshiro", - "serde", - "version_check", -] - -[[package]] -name = "imbl-sized-chunks" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4241005618a62f8d57b2febd02510fb96e0137304728543dfc5fd6f052c22d" -dependencies = [ - "bitmaps", -] - -[[package]] -name = "impartial-ord" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab604ee7085efba6efc65e4ebca0e9533e3aff6cb501d7d77b211e3a781c6d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "include_dir" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" -dependencies = [ - "include_dir_macros", -] - -[[package]] -name = "include_dir_macros" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", -] - -[[package]] -name = "inotify" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" -dependencies = [ - "bitflags 2.11.0", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "js_int" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d937f95470b270ce8b8950207715d71aa8e153c0d44c6684d59397ed4949160a" -dependencies = [ - "serde", -] - -[[package]] -name = "js_option" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7dd3e281add16813cf673bf74a32249b0aa0d1c8117519a17b3ada5e8552b3c" -dependencies = [ - "serde_core", -] - -[[package]] -name = "konst" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" -dependencies = [ - "const_panic", - "konst_kernel", - "typewit", -] - -[[package]] -name = "konst_kernel" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" -dependencies = [ - "typewit", -] - -[[package]] -name = "kqueue" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" - -[[package]] -name = "libredox" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" -dependencies = [ - "bitflags 2.11.0", - "libc", - "plain", - "redox_syscall 0.7.3", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "macroific" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89f276537b4b8f981bf1c13d79470980f71134b7bdcc5e6e911e910e556b0285" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "macroific_macro", -] - -[[package]] -name = "macroific_attr_parse" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad4023761b45fcd36abed8fb7ae6a80456b0a38102d55e89a57d9a594a236be9" -dependencies = [ - "proc-macro2", - "quote", - "sealed", - "syn 2.0.117", -] - -[[package]] -name = "macroific_core" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a7594d3c14916fa55bef7e9d18c5daa9ed410dd37504251e4b75bbdeec33e3" -dependencies = [ - "proc-macro2", - "quote", - "sealed", - "syn 2.0.117", -] - -[[package]] -name = "macroific_macro" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da6f2ed796261b0a74e2b52b42c693bb6dee1effba3a482c49592659f824b3b" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "markup5ever" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3" -dependencies = [ - "log", - "tendril", - "web_atoms", -] - -[[package]] -name = "match_token" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "matrix-pickle" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c34e6db65145740459f2ca56623b40cd4e6000ffae2a7d91515fa82aa935dbf" -dependencies = [ - "matrix-pickle-derive", - "thiserror 2.0.18", -] - -[[package]] -name = "matrix-pickle-derive" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a962fc9981f823f6555416dcb2ae9ae67ca412d767ee21ecab5150113ee6285b" -dependencies = [ - "proc-macro-crate", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "matrix-sdk" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33f9bc45edd7f8e25161521fdd30654da5c55e6749be6afa1aa9d6cf838ace0" -dependencies = [ - "anymap2", - "aquamarine", - "as_variant", - "async-channel", - "async-stream", - "async-trait", - "backon", - "bytes", - "bytesize", - "cfg-if", - "event-listener", - "eyeball", - "eyeball-im", - "futures-core", - "futures-util", - "gloo-timers", - "http", - "imbl", - "indexmap", - "itertools 0.14.0", - "js_int", - "language-tags", - "matrix-sdk-base", - "matrix-sdk-common", - "matrix-sdk-indexeddb", - "matrix-sdk-sqlite", - "mime", - "mime2ext", - "oauth2", - "once_cell", - "percent-encoding", - "pin-project-lite", - "reqwest 0.12.28", - "ruma", - "serde", - "serde_html_form", - "serde_json", - "sha2", - "tempfile", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "url", - "urlencoding", - "vodozemac", - "zeroize", -] - -[[package]] -name = "matrix-sdk-base" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f404a390ff98a73c426b1496b169be60ce6a93723a9a664e579d978a84c5e4" -dependencies = [ - "as_variant", - "async-trait", - "bitflags 2.11.0", - "decancer", - "eyeball", - "eyeball-im", - "futures-util", - "growable-bloom-filter", - "matrix-sdk-common", - "matrix-sdk-crypto", - "matrix-sdk-store-encryption", - "once_cell", - "regex", - "ruma", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "unicode-normalization", -] - -[[package]] -name = "matrix-sdk-common" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54fae2bdfc3d760d21a84d6d2036b5db5c48d9a3dee3794119e3fb9c4cc4ccc5" -dependencies = [ - "eyeball-im", - "futures-core", - "futures-executor", - "futures-util", - "gloo-timers", - "imbl", - "ruma", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "tracing-subscriber", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "matrix-sdk-crypto" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "304fc576810a9618bb831c4ad6403c758ec424f677668a49a196e3cde4b8f99f" -dependencies = [ - "aes", - "aquamarine", - "as_variant", - "async-trait", - "bs58", - "byteorder", - "cfg-if", - "ctr", - "eyeball", - "futures-core", - "futures-util", - "hkdf", - "hmac", - "itertools 0.14.0", - "js_option", - "matrix-sdk-common", - "pbkdf2", - "rand 0.8.5", - "rmp-serde", - "ruma", - "serde", - "serde_json", - "sha2", - "subtle", - "thiserror 2.0.18", - "time", - "tokio", - "tokio-stream", - "tracing", - "ulid", - "url", - "vodozemac", - "zeroize", -] - -[[package]] -name = "matrix-sdk-indexeddb" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6096084cc8d339c03e269ca25534d0f1e88d0097c35a215eb8c311797ec3e9" -dependencies = [ - "async-trait", - "base64", - "futures-util", - "getrandom 0.2.17", - "gloo-utils", - "hkdf", - "js-sys", - "matrix-sdk-base", - "matrix-sdk-crypto", - "matrix-sdk-store-encryption", - "matrix_indexed_db_futures", - "rmp-serde", - "ruma", - "serde", - "serde-wasm-bindgen", - "serde_json", - "sha2", - "thiserror 2.0.18", - "tokio", - "tracing", - "uuid", - "wasm-bindgen", - "web-sys", - "zeroize", -] - -[[package]] -name = "matrix-sdk-sqlite" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4325742fc06b7f75c80eec39e8fb32b06ea4b09b7aa1d432b67b01d08fbacc28" -dependencies = [ - "as_variant", - "async-trait", - "deadpool", - "deadpool-sync", - "itertools 0.14.0", - "matrix-sdk-base", - "matrix-sdk-crypto", - "matrix-sdk-store-encryption", - "num_cpus", - "rmp-serde", - "ruma", - "rusqlite", - "serde", - "serde_json", - "serde_path_to_error", - "thiserror 2.0.18", - "tokio", - "tracing", - "vodozemac", - "zeroize", -] - -[[package]] -name = "matrix-sdk-store-encryption" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162a93e83114d5cef25c0ebaea72aa01b9f233df6ec4a2af45f175d01ec26323" -dependencies = [ - "base64", - "blake3", - "chacha20poly1305", - "getrandom 0.2.17", - "hmac", - "pbkdf2", - "rand 0.8.5", - "rmp-serde", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.18", - "zeroize", -] - -[[package]] -name = "matrix_indexed_db_futures" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245ff6a224b4df7b0c90dda2dd5a6eb46112708d49e8bdd8b007fccb09fea8e4" -dependencies = [ - "accessory", - "cfg-if", - "delegate-display", - "derive_more 2.1.1", - "fancy_constructor", - "futures-core", - "js-sys", - "matrix_indexed_db_futures_macros_internal", - "sealed", - "serde", - "serde-wasm-bindgen", - "smallvec", - "thiserror 2.0.18", - "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm_evt_listener", - "web-sys", - "web-time", -] - -[[package]] -name = "matrix_indexed_db_futures_macros_internal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b428aee5c0fe9e5babd29e99d289b7f64718c444989aac0442d1fd6d3e3f66d1" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime2ext" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf6f36070878c42c5233846cd3de24cf9016828fd47bc22957a687298bb21fc" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "mockito" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90820618712cab19cfc46b274c6c22546a82affcb3c3bdf0f29e3db8e1bb92c0" -dependencies = [ - "assert-json-diff", - "bytes", - "colored", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "log", - "pin-project-lite", - "rand 0.9.2", - "regex", - "serde_json", - "serde_urlencoded", - "similar", - "tokio", -] - -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http", - "httparse", - "memchr", - "mime", - "spin", - "tokio", - "version_check", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases 0.1.1", - "libc", -] - -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases 0.2.1", - "libc", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "notify" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" -dependencies = [ - "bitflags 2.11.0", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio", - "notify-types", - "walkdir", - "windows-sys 0.60.2", -] - -[[package]] -name = "notify-types" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "oauth2" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" -dependencies = [ - "base64", - "chrono", - "getrandom 0.2.17", - "http", - "rand 0.8.5", - "reqwest 0.12.28", - "serde", - "serde_json", - "serde_path_to_error", - "sha2", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link 0.2.1", -] - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "poem" -version = "3.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f977080932c87287147dca052951c3e2696f8759863f6b4e4c0c9ffe7a4cc8b" -dependencies = [ - "base64", - "bytes", - "futures-util", - "headers", - "http", - "http-body-util", - "hyper", - "hyper-util", - "mime", - "multer", - "nix 0.30.1", - "parking_lot", - "percent-encoding", - "pin-project-lite", - "poem-derive", - "quick-xml", - "regex", - "rfc7239", - "serde", - "serde_json", - "serde_urlencoded", - "serde_yaml", - "smallvec", - "sse-codec", - "sync_wrapper", - "tempfile", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-tungstenite 0.27.0", - "tokio-util", - "tracing", - "wildmatch", -] - -[[package]] -name = "poem-derive" -version = "3.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056e2fea6de1cb240ffe23cfc4fc370b629f8be83b5f27e16b7acd5231a72de4" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "poem-openapi" -version = "5.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ccbcc395bf4dd03df1da32da351b6b6732e4074ce27ddec315650e52a2be44c" -dependencies = [ - "base64", - "bytes", - "derive_more 2.1.1", - "futures-util", - "indexmap", - "itertools 0.14.0", - "mime", - "num-traits", - "poem", - "poem-openapi-derive", - "quick-xml", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "serde_yaml", - "thiserror 2.0.18", - "tokio", -] - -[[package]] -name = "poem-openapi-derive" -version = "5.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41273b691a3d467a8c44d05506afba9f7b6bd56c9cdf80123de13fe52d7ec587" -dependencies = [ - "darling", - "http", - "indexmap", - "mime", - "proc-macro-crate", - "proc-macro2", - "quote", - "regex", - "syn 2.0.117", - "thiserror 2.0.18", -] - -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "portable-pty" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e" -dependencies = [ - "anyhow", - "bitflags 1.3.2", - "downcast-rs", - "filedescriptor", - "lazy_static", - "libc", - "log", - "nix 0.28.0", - "serial2", - "shared_library", - "shell-words", - "winapi", - "winreg", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pulldown-cmark" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6" -dependencies = [ - "bitflags 2.11.0", - "memchr", - "pulldown-cmark-escape", - "unicase", -] - -[[package]] -name = "pulldown-cmark-escape" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" - -[[package]] -name = "quick-xml" -version = "0.36.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases 0.2.1", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases 0.2.1", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xoshiro" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" -dependencies = [ - "rand_core 0.9.5", -] - -[[package]] -name = "readlock" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da6f291b23556edd9edaf655a0be2ad8ef8002ff5f1bca62b264f3f58b53f34" - -[[package]] -name = "readlock-tokio" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7e264f9ec4f3d112e8e2f214e8e7cb5cf3b83278f3570b7e00bfe13d3bd8ff" -dependencies = [ - "tokio", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams 0.4.2", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams 0.5.0", - "web-sys", -] - -[[package]] -name = "rfc7239" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a82f1d1e38e9a85bb58ffcfadf22ed6f2c94e8cd8581ec2b0f80a2a6858350f" -dependencies = [ - "uncased", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rmp" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "rmp-serde" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" -dependencies = [ - "rmp", - "serde", -] - -[[package]] -name = "ruma" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f620a2116d0d3082f9256e61dcdf67f2ec266d3f6bb9d2f9c8a20ec5a1fabb" -dependencies = [ - "assign", - "js_int", - "js_option", - "ruma-client-api", - "ruma-common", - "ruma-events", - "ruma-federation-api", - "ruma-html", - "web-time", -] - -[[package]] -name = "ruma-client-api" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc977d1a91ea15dcf896cbd7005ed4a253784468833638998109ffceaee53e7" -dependencies = [ - "as_variant", - "assign", - "bytes", - "date_header", - "http", - "js_int", - "js_option", - "maplit", - "ruma-common", - "ruma-events", - "serde", - "serde_html_form", - "serde_json", - "thiserror 2.0.18", - "url", - "web-time", -] - -[[package]] -name = "ruma-common" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a01993f22d291320b7c9267675e7395775e95269ff526e2c8c3ed5e13175b" -dependencies = [ - "as_variant", - "base64", - "bytes", - "form_urlencoded", - "getrandom 0.2.17", - "http", - "indexmap", - "js-sys", - "js_int", - "konst", - "percent-encoding", - "rand 0.8.5", - "regex", - "ruma-identifiers-validation", - "ruma-macros", - "serde", - "serde_html_form", - "serde_json", - "thiserror 2.0.18", - "time", - "tracing", - "url", - "uuid", - "web-time", - "wildmatch", - "zeroize", -] - -[[package]] -name = "ruma-events" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dbdeccb62cb4ffe3282325de8ba28cbc0fdce7c78a3f11b7241fbfdb9cb9907" -dependencies = [ - "as_variant", - "indexmap", - "js_int", - "js_option", - "percent-encoding", - "regex", - "ruma-common", - "ruma-identifiers-validation", - "ruma-macros", - "serde", - "serde_json", - "thiserror 2.0.18", - "tracing", - "url", - "web-time", - "wildmatch", - "zeroize", -] - -[[package]] -name = "ruma-federation-api" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb45c15badbf4299c6113a6b90df3e7cb64edbe756bbd8e0224144b56b38305" -dependencies = [ - "headers", - "http", - "http-auth", - "js_int", - "mime", - "ruma-common", - "ruma-events", - "ruma-signatures", - "serde", - "serde_json", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "ruma-html" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6dcd6e9823e177d15460d3cd3a413f38a2beea381f26aca1001c05cd6954ff" -dependencies = [ - "as_variant", - "html5ever", - "tracing", - "wildmatch", -] - -[[package]] -name = "ruma-identifiers-validation" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9c6b5643060beec0fc9d7acfb41d2c5d91e1591db440ff62361d178e77c35fe" -dependencies = [ - "js_int", - "thiserror 2.0.18", -] - -[[package]] -name = "ruma-macros" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0753312ad577ac462de1742bf2e326b6ba9856ff6f13343aeb17d423fd5426" -dependencies = [ - "as_variant", - "cfg-if", - "proc-macro-crate", - "proc-macro2", - "quote", - "ruma-identifiers-validation", - "serde", - "syn 2.0.117", - "toml 0.9.12+spec-1.1.0", -] - -[[package]] -name = "ruma-signatures" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146ace2cd59b60ec80d3e801a84e7e6a91e3e01d18a9f5d896ea7ca16a6b8e08" -dependencies = [ - "base64", - "ed25519-dalek", - "pkcs8", - "rand 0.8.5", - "ruma-common", - "serde_json", - "sha2", - "thiserror 2.0.18", -] - -[[package]] -name = "rusqlite" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" -dependencies = [ - "bitflags 2.11.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - -[[package]] -name = "rust-embed" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.117", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" -dependencies = [ - "sha2", - "walkdir", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -dependencies = [ - "aws-lc-rs", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sealed" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_html_form" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" -dependencies = [ - "form_urlencoded", - "indexmap", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_spanned" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "serial2" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1401f562d358cdfdbdf8946e51a7871ede1db68bd0fd99bedc79e400241550" -dependencies = [ - "cfg-if", - "libc", - "winapi", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shared_library" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -dependencies = [ - "lazy_static", - "libc", -] - -[[package]] -name = "shell-words" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" - -[[package]] -name = "similar" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" - -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sse-codec" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a395a858c7ff5c4b42aeab0501e07c978ac5e1ae5059f301884dab3fa405f47" -dependencies = [ - "futures-io", - "futures_codec", - "memchr", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "storkit" -version = "0.4.1" -dependencies = [ - "async-stream", - "async-trait", - "bytes", - "chrono", - "eventsource-stream", - "filetime", - "futures", - "homedir", - "ignore", - "libsqlite3-sys", - "matrix-sdk", - "mime_guess", - "mockito", - "notify", - "poem", - "poem-openapi", - "portable-pty", - "pulldown-cmark", - "reqwest 0.13.2", - "rust-embed", - "serde", - "serde_json", - "serde_urlencoded", - "serde_yaml", - "strip-ansi-escapes", - "tempfile", - "tokio", - "tokio-tungstenite 0.29.0", - "toml 1.0.7+spec-1.1.0", - "uuid", - "wait-timeout", - "walkdir", -] - -[[package]] -name = "string_cache" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", -] - -[[package]] -name = "strip-ansi-escapes" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" -dependencies = [ - "vte", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.27.0", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.29.0", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "serde_core", - "serde_spanned", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "winnow 0.7.15", -] - -[[package]] -name = "toml" -version = "1.0.7+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime 1.0.1+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 1.0.0", -] - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_datetime" -version = "1.0.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.25.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1" -dependencies = [ - "indexmap", - "toml_datetime 1.0.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.0", -] - -[[package]] -name = "toml_parser" -version = "1.0.10+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" -dependencies = [ - "winnow 1.0.0", -] - -[[package]] -name = "toml_writer" -version = "1.0.7+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d" - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "async-compression", - "bitflags 2.11.0", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "iri-string", - "pin-project-lite", - "tokio", - "tokio-util", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.2", - "sha1", - "thiserror 2.0.18", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.2", - "sha1", - "thiserror 2.0.18", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "typewit" -version = "1.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71" -dependencies = [ - "typewit_proc_macros", -] - -[[package]] -name = "typewit_proc_macros" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" - -[[package]] -name = "ulid" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" -dependencies = [ - "rand 0.9.2", - "web-time", -] - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", - "serde_derive", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" -dependencies = [ - "getrandom 0.4.2", - "js-sys", - "serde_core", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vodozemac" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022a277687e4e8685d72b95a7ca3ccfec907daa946678e715f8badaa650883d" -dependencies = [ - "aes", - "arrayvec", - "base64", - "base64ct", - "cbc", - "chacha20poly1305", - "curve25519-dalek", - "ed25519-dalek", - "getrandom 0.2.17", - "hkdf", - "hmac", - "matrix-pickle", - "prost", - "rand 0.8.5", - "serde", - "serde_bytes", - "serde_json", - "sha2", - "subtle", - "thiserror 2.0.18", - "x25519-dalek", - "zeroize", -] - -[[package]] -name = "vte" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" -dependencies = [ - "memchr", -] - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm-streams" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm_evt_listener" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc92d6378b411ed94839112a36d9dbc77143451d85b05dfb0cce93a78dab1963" -dependencies = [ - "accessory", - "derivative", - "derive_more 1.0.0", - "fancy_constructor", - "futures-core", - "js-sys", - "smallvec", - "tokio", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "web-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "web_atoms" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" -dependencies = [ - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - -[[package]] -name = "wildmatch" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29333c3ea1ba8b17211763463ff24ee84e41c78224c16b001cd907e663a38c68" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link 0.2.1", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" - -[[package]] -name = "winnow" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core 0.6.4", - "serde", - "zeroize", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" - -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index f9b6dc8..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[workspace] -members = ["server"] -resolver = "3" - -[workspace.dependencies] -async-stream = "0.3" -async-trait = "0.1.89" -bytes = "1" -chrono = { version = "0.4.44", features = ["serde"] } -eventsource-stream = "0.2.3" -futures = "0.3" -homedir = "0.3.6" -ignore = "0.4.25" -mime_guess = "2" -notify = "8.2.0" -poem = { version = "3", features = ["websocket", "test"] } -poem-openapi = { version = "5", features = ["swagger-ui"] } -portable-pty = "0.9.0" -reqwest = { version = "0.13.2", features = ["json", "stream"] } -rust-embed = "8" -serde = { version = "1", features = ["derive"] } -serde_json = "1" -serde_urlencoded = "0.7" -serde_yaml = "0.9" -strip-ansi-escapes = "0.2" -tempfile = "3" -tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync"] } -toml = "1.0.7" -uuid = { version = "1.22.0", features = ["v4", "serde"] } -tokio-tungstenite = "0.29.0" -walkdir = "2.5.0" -filetime = "0.2" -matrix-sdk = { version = "0.16.0", default-features = false, features = [ - "rustls-tls", - "sqlite", - "e2e-encryption", -] } -pulldown-cmark = { version = "0.13.1", default-features = false, features = [ - "html", -] } diff --git a/README.md b/README.md deleted file mode 100644 index bd65587..0000000 --- a/README.md +++ /dev/null @@ -1,182 +0,0 @@ -# Story Kit - -This app runs as a single Rust web server binary that serves the Vite/React frontend and exposes APIs. -The frontend lives in the `frontend/` directory. - -You can also run the frontend and backend separately in development (Vite dev server + Rust API). - -## Running it in development - -```bash -# Build the frontend -cd frontend -npm install -npm run dev - -# In another terminal - run the server (serves embedded frontend/dist/) -cargo run -``` - -## Production - -```bash -# Build the release binary (also builds the frontend via build.rs) -cargo build --release - -# Run the server (serves embedded frontend/dist/) -./target/release/storkit -``` - -## Cross-Platform Distribution - -Story Kit ships as a **single self-contained binary** with the React frontend embedded via -`rust-embed`. No Rust toolchain, Node.js, or extra libraries are required on the target machine. - -### macOS - -```bash -# Native build – no extra tools required beyond Rust + npm -make build-macos -# Output: target/release/storkit - -# Verify only system frameworks are linked (Security.framework, libSystem.B.dylib, etc.) -otool -L target/release/storkit -``` - -### Linux (static x86_64, zero dynamic deps) - -The Linux build uses the `x86_64-unknown-linux-musl` target to produce a fully static binary. - -**Prerequisites:** - -```bash -# Install cross – a Rust cross-compilation tool backed by Docker -cargo install cross - -# Ensure Docker Desktop (or Docker Engine) is running -``` - -**Build:** - -```bash -make build-linux -# Output: target/x86_64-unknown-linux-musl/release/storkit - -# Verify the binary is statically linked -file target/x86_64-unknown-linux-musl/release/storkit -# Expected: ELF 64-bit LSB executable, x86-64, statically linked - -ldd target/x86_64-unknown-linux-musl/release/storkit -# Expected: not a dynamic executable -``` - -**Running on any Linux x86_64 machine:** - -```bash -# No Rust, Node, glibc, or any other library needed – just copy and run -./storkit -``` - -## Releasing - -Builds both macOS and Linux binaries locally, tags the repo, and publishes a Gitea release with a changelog. - -**One-time setup:** - -1. Create a Gitea API token at `https://code.crashlabs.io/user/settings/applications` (needs repository read/write) -2. Add it to `.env` (gitignored): `GITEA_TOKEN=your_token` -3. Ensure `cross` is installed (`cargo install cross`) and Docker is running - -**To release:** - -```bash -make release V=0.2.0 -``` - -This will: -- Build macOS arm64 (native) and Linux amd64 (static musl via cross/Docker) -- Generate a changelog from commits since the last tag -- Tag the repo as `v0.2.0` and push the tag -- Create a Gitea release with both binaries and the changelog attached - -## Testing - -### Frontend Tests - -The frontend uses **Vitest** for unit tests and **Playwright** for end-to-end tests. - -```bash -cd frontend - -# Run unit tests -npm test - -# Run end-to-end tests -npm run test:e2e -``` - -### Backend Tests - -This project uses **nextest** for running tests and **cargo-llvm-cov** for code coverage. - -### Install Tools - -```bash -cargo install cargo-nextest cargo-llvm-cov -``` - -### Run Tests - -```bash -# Run all tests -cargo nextest run - -# Run specific module -cargo nextest run search_files - -# Run with verbose output -cargo nextest run --no-capture -``` - -### Generate Coverage - -```bash -# HTML report (opens in browser) -cargo llvm-cov nextest --html --open - -# Terminal output -cargo llvm-cov nextest - -# LCOV format (for CI) -cargo llvm-cov nextest --lcov --output-path lcov.info - -# Clean coverage data -cargo llvm-cov clean -``` - -### Configuration - -- **Nextest config**: `.config/nextest.toml` -- **Coverage output**: `target/llvm-cov/html/index.html` - -## Current Coverage (search_files module) - -``` -Module: commands/search.rs -├── Region Coverage: 75.36% -├── Function Coverage: 69.05% -└── Line Coverage: 72.55% -``` - -### Available Test Profiles - -```bash -# Development (default) -cargo nextest run - -# CI with retries -cargo nextest run --profile ci - -# Coverage optimized -cargo nextest run --profile coverage -``` diff --git a/biome.json b/biome.json deleted file mode 100644 index 0cdceba..0000000 --- a/biome.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.3.15/schema.json", - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": true - }, - "files": { - "includes": ["frontend/**"] - }, - "formatter": { - "enabled": true, - "indentStyle": "tab" - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - }, - "javascript": { - "formatter": { - "quoteStyle": "double" - } - }, - "assist": { - "enabled": true, - "actions": { - "source": { - "organizeImports": "on" - } - } - } -} diff --git a/docker/.dockerignore b/docker/.dockerignore deleted file mode 100644 index ec606cf..0000000 --- a/docker/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -# Docker build context exclusions -**/target/ -**/node_modules/ -frontend/dist/ -.storkit/worktrees/ -.storkit/logs/ -.storkit/work/6_archived/ -.git/ -*.swp -*.swo -.DS_Store diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index daf5ed0..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,132 +0,0 @@ -# Story Kit – single-container runtime -# All components (server, agents, web UI) run inside this container. -# The target project repo is bind-mounted at /workspace. -# -# Build: docker build -t storkit -f docker/Dockerfile . -# Run: docker compose -f docker/docker-compose.yml up -# -# Tested with: OrbStack (recommended on macOS), Docker Desktop (slower bind mounts) - -FROM rust:1.90-bookworm AS base - -# Clippy is needed at runtime for acceptance gates (cargo clippy) -RUN rustup component add clippy - -# ── System deps ────────────────────────────────────────────────────── -RUN apt-get update && apt-get install -y --no-install-recommends \ - git \ - curl \ - ca-certificates \ - build-essential \ - pkg-config \ - libssl-dev \ - # cargo-nextest is a pre-built binary - && rm -rf /var/lib/apt/lists/* - -# ── Node.js 22.x (matches host) ───────────────────────────────────── -RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ - && apt-get install -y --no-install-recommends nodejs \ - && rm -rf /var/lib/apt/lists/* - -# ── cargo-nextest (test runner) ────────────────────────────────────── -RUN curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C /usr/local/bin - -# ── Claude Code CLI ────────────────────────────────────────────────── -# Claude Code is distributed as an npm global package. -# The CLI binary is `claude`. -RUN npm install -g @anthropic-ai/claude-code - -# ── Working directory ──────────────────────────────────────────────── -# /app holds the storkit source (copied in at build time for the binary). -# /workspace is where the target project repo gets bind-mounted at runtime. -WORKDIR /app - -# ── Build the storkit server binary ───────────────────────────────── -# Copy the full project tree so `cargo build` and `npm run build` (via -# build.rs) can produce the release binary with embedded frontend assets. -COPY . . - -# Build frontend deps first (better layer caching) -RUN cd frontend && npm ci - -# Build the release binary (build.rs runs npm run build for the frontend) -RUN cargo build --release \ - && cp target/release/storkit /usr/local/bin/storkit - -# ── Runtime stage (smaller image) ─────────────────────────────────── -FROM debian:bookworm-slim AS runtime - -RUN apt-get update && apt-get install -y --no-install-recommends \ - git \ - curl \ - ca-certificates \ - libssl3 \ - # build-essential (gcc/cc) needed at runtime for: - # - rebuild_and_restart (cargo build --release) - # - agent-driven cargo commands (clippy, test, build) - build-essential \ - pkg-config \ - libssl-dev \ - # procps provides ps, needed by tests and process management - procps \ - && rm -rf /var/lib/apt/lists/* - -# Node.js in runtime -RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ - && apt-get install -y --no-install-recommends nodejs \ - && rm -rf /var/lib/apt/lists/* - -# Claude Code CLI in runtime -RUN npm install -g @anthropic-ai/claude-code - -# Cargo and Rust toolchain needed at runtime for: -# - rebuild_and_restart (cargo build inside the container) -# - Agent-driven cargo commands (cargo clippy, cargo test, etc.) -COPY --from=base /usr/local/cargo /usr/local/cargo -COPY --from=base /usr/local/rustup /usr/local/rustup -ENV PATH="/usr/local/cargo/bin:${PATH}" -ENV RUSTUP_HOME="/usr/local/rustup" -ENV CARGO_HOME="/usr/local/cargo" - -# cargo-nextest -COPY --from=base /usr/local/bin/cargo-nextest /usr/local/bin/cargo-nextest - -# The storkit binary -COPY --from=base /usr/local/bin/storkit /usr/local/bin/storkit - -# Copy the full source tree so rebuild_and_restart can do `cargo build` -# from the workspace root (CARGO_MANIFEST_DIR is baked into the binary). -# Alternative: mount the source as a volume. -COPY --from=base /app /app - -# ── Non-root user ──────────────────────────────────────────────────── -# Claude Code refuses --dangerously-skip-permissions (bypassPermissions) -# when running as root. Create a dedicated user so agents can launch. -RUN groupadd -r storkit \ - && useradd -r -g storkit -m -d /home/storkit storkit \ - && mkdir -p /home/storkit/.claude \ - && chown -R storkit:storkit /home/storkit \ - && chown -R storkit:storkit /usr/local/cargo /usr/local/rustup \ - && chown -R storkit:storkit /app \ - && mkdir -p /workspace/target /app/target \ - && chown storkit:storkit /workspace/target /app/target - -# ── Entrypoint ─────────────────────────────────────────────────────── -# Validates required env vars (GIT_USER_NAME, GIT_USER_EMAIL) and -# configures git identity before starting the server. -COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh - -USER storkit -WORKDIR /workspace - -# ── Ports ──────────────────────────────────────────────────────────── -# Web UI + MCP server -EXPOSE 3001 - -# ── Volumes (defined in docker-compose.yml) ────────────────────────── -# /workspace – bind mount: target project repo -# /home/storkit/.claude – named volume: Claude Code sessions/state -# /usr/local/cargo/registry – named volume: cargo dependency cache - -ENTRYPOINT ["entrypoint.sh"] -CMD ["storkit", "/workspace"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml deleted file mode 100644 index 1bb6817..0000000 --- a/docker/docker-compose.yml +++ /dev/null @@ -1,118 +0,0 @@ -# Story Kit – single-container deployment -# -# Usage: -# # Set your API key and project path, then: -# ANTHROPIC_API_KEY=sk-ant-... PROJECT_PATH=/path/to/your/repo \ -# docker compose -f docker/docker-compose.yml up -# -# OrbStack users: just install OrbStack and use `docker compose` normally. -# OrbStack's VirtioFS bind mount driver is significantly faster than -# Docker Desktop's default (see spike findings). - -services: - storkit: - build: - context: .. - dockerfile: docker/Dockerfile - container_name: storkit - ports: - # Bind to localhost only — not exposed on all interfaces. - - "127.0.0.1:3001:3001" - environment: - # Optional: Anthropic API key. If unset, Claude Code falls back to - # OAuth credentials from `claude login` (e.g. Max subscription). - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} - # Required: git identity for agent commits - - GIT_USER_NAME=${GIT_USER_NAME:?Set GIT_USER_NAME} - - GIT_USER_EMAIL=${GIT_USER_EMAIL:?Set GIT_USER_EMAIL} - # Optional: override the server port (default 3001) - - STORKIT_PORT=3001 - # Optional: Matrix bot credentials (if using Matrix integration) - - MATRIX_HOMESERVER=${MATRIX_HOMESERVER:-} - - MATRIX_USER=${MATRIX_USER:-} - - MATRIX_PASSWORD=${MATRIX_PASSWORD:-} - # Optional: Slack webhook (if using Slack integration) - - SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN:-} - - SLACK_APP_TOKEN=${SLACK_APP_TOKEN:-} - volumes: - # The target project repo – bind-mounted from host. - # Changes made by agents inside the container are immediately - # visible on the host (and vice versa). - - ${PROJECT_PATH:?Set PROJECT_PATH}:/workspace - - # Cargo registry cache – persists downloaded crates across - # container restarts so `cargo build` doesn't re-download. - - cargo-registry:/usr/local/cargo/registry - - # Cargo git checkouts – persists git-based dependencies. - - cargo-git:/usr/local/cargo/git - - # Claude Code state – persists session history, projects config, - # and conversation transcripts so --resume works across restarts. - - claude-state:/home/storkit/.claude - - # Storkit source tree for rebuild_and_restart. - # The binary has CARGO_MANIFEST_DIR baked in at compile time - # pointing to /app/server, so the source must be at /app. - # This is COPY'd in the Dockerfile; mounting over it allows - # live source updates without rebuilding the image. - # Mount host source so rebuild_and_restart picks up live changes: - - ./..:/app - - # Keep cargo build artifacts off the bind mount. - # Bind-mount directory traversal is ~23x slower than Docker volumes - # (confirmed in spike 329). Cargo stat-checks every file in target/ - # on incremental builds — leaving it on the bind mount makes builds - # catastrophically slow (~12s just to traverse the tree). - - workspace-target:/workspace/target - - storkit-target:/app/target - - # ── Security hardening ────────────────────────────────────────── - # Read-only root filesystem. Only explicitly mounted volumes and - # tmpfs paths are writable. - read_only: true - tmpfs: - - /tmp:size=512M,exec - - /home/storkit:size=512M,uid=999,gid=999,exec - - # Drop all Linux capabilities, then add back only what's needed. - # SETUID/SETGID needed by Claude Code's PTY allocation (openpty). - cap_drop: - - ALL - cap_add: - - SETUID - - SETGID - - # Prevent child processes from gaining new privileges via setuid, - # setgid, or other mechanisms. - security_opt: - - no-new-privileges:true - - # Resource limits – cap the whole system. - # Adjust based on your machine. These are conservative defaults. - deploy: - resources: - limits: - cpus: "4" - memory: 8G - reservations: - cpus: "1" - memory: 2G - - # Health check – verify the MCP endpoint responds - healthcheck: - test: ["CMD", "curl", "-sf", "http://localhost:3001/health"] - interval: 30s - timeout: 5s - retries: 3 - start_period: 10s - - # Restart policy – restart on crash but not on manual stop - restart: unless-stopped - -volumes: - cargo-registry: - cargo-git: - claude-state: - workspace-target: - storkit-target: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh deleted file mode 100755 index 6cb0e93..0000000 --- a/docker/entrypoint.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -set -e - -# ── Git identity ───────────────────────────────────────────────────── -# Agents commit code inside the container. Without a git identity, -# commits fail or use garbage defaults. Fail loudly at startup so the -# operator knows immediately. -if [ -z "$GIT_USER_NAME" ]; then - echo "FATAL: GIT_USER_NAME is not set. Export it in your environment or docker-compose.yml." >&2 - exit 1 -fi -if [ -z "$GIT_USER_EMAIL" ]; then - echo "FATAL: GIT_USER_EMAIL is not set. Export it in your environment or docker-compose.yml." >&2 - exit 1 -fi - -# Use GIT_AUTHOR/COMMITTER env vars instead of git config --global, -# so the root filesystem can stay read-only (no ~/.gitconfig write). -export GIT_AUTHOR_NAME="$GIT_USER_NAME" -export GIT_COMMITTER_NAME="$GIT_USER_NAME" -export GIT_AUTHOR_EMAIL="$GIT_USER_EMAIL" -export GIT_COMMITTER_EMAIL="$GIT_USER_EMAIL" - -exec "$@" diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index 9028bf7..0000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -coverage -*.local diff --git a/frontend/index.html b/frontend/index.html deleted file mode 100644 index a1d2e54..0000000 --- a/frontend/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Storkit - - - -
- - - diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 978db4d..0000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,8408 +0,0 @@ -{ - "name": "living-spec-standalone", - "version": "0.4.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "living-spec-standalone", - "version": "0.4.1", - "dependencies": { - "@types/react-syntax-highlighter": "^15.5.13", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-markdown": "^10.1.0", - "react-syntax-highlighter": "^16.1.0" - }, - "devDependencies": { - "@biomejs/biome": "^2.4.2", - "@playwright/test": "^1.47.2", - "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^16.0.0", - "@testing-library/user-event": "^14.4.3", - "@types/node": "^25.0.0", - "@types/react": "^19.1.8", - "@types/react-dom": "^19.1.6", - "@vitejs/plugin-react": "^4.6.0", - "@vitest/coverage-v8": "^2.1.9", - "jest": "^29.0.0", - "jsdom": "^28.1.0", - "ts-jest": "^29.0.0", - "typescript": "~5.8.3", - "vite": "^5.4.21", - "vitest": "^2.1.4" - } - }, - "node_modules/@acemir/cssom": { - "version": "0.9.31", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", - "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@adobe/css-tools": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", - "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@asamuzakjp/css-color": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", - "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^3.0.0", - "@csstools/css-color-parser": "^4.0.1", - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.5" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@asamuzakjp/dom-selector": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", - "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/nwsapi": "^2.3.9", - "bidi-js": "^1.0.3", - "css-tree": "^3.1.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.6" - } - }, - "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@biomejs/biome": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.3.tgz", - "integrity": "sha512-cBrjf6PNF6yfL8+kcNl85AjiK2YHNsbU0EvDOwiZjBPbMbQ5QcgVGFpjD0O52p8nec5O8NYw7PKw3xUR7fPAkQ==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.3", - "@biomejs/cli-darwin-x64": "2.4.3", - "@biomejs/cli-linux-arm64": "2.4.3", - "@biomejs/cli-linux-arm64-musl": "2.4.3", - "@biomejs/cli-linux-x64": "2.4.3", - "@biomejs/cli-linux-x64-musl": "2.4.3", - "@biomejs/cli-win32-arm64": "2.4.3", - "@biomejs/cli-win32-x64": "2.4.3" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.3.tgz", - "integrity": "sha512-eOafSFlI/CF4id2tlwq9CVHgeEqvTL5SrhWff6ZORp6S3NL65zdsR3ugybItkgF8Pf4D9GSgtbB6sE3UNgOM9w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.3.tgz", - "integrity": "sha512-V2+av4ilbWcBMNufTtMMXVW00nPwyIjI5qf7n9wSvUaZ+tt0EvMGk46g9sAFDJBEDOzSyoRXiSP6pCvKTOEbPA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.3.tgz", - "integrity": "sha512-0m+O0x9FgK99FAwDK+fiDtjs2wnqq7bvfj17KJVeCkTwT/liI+Q9njJG7lwXK0iSJVXeFNRIxukpVI3SifMYAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.3.tgz", - "integrity": "sha512-QuFzvsGo8BA4Xm7jGX5idkw6BqFblcCPySMTvq0AhGYnhUej5VJIDJbmTKfHqwjHepZiC4fA+T5i6wmiZolZNw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.3.tgz", - "integrity": "sha512-NVqh0saIU0u5OfOp/0jFdlKRE59+XyMvWmtx0f6Nm/2OpdxBl04coRIftBbY9d1gfu+23JVv4CItAqPYrjYh5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.3.tgz", - "integrity": "sha512-qEc0OCpj/uytruQ4wLM0yWNJLZy0Up8H1Er5MW3SrstqM6J2d4XqdNA86xzCy8MQCHpoVZ3lFye3GBlIL4/ljw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.3.tgz", - "integrity": "sha512-gRO96vrIARilv/Cp2ZnmNNL5LSZg3RO75GPp13hsLO3N4YVpE7saaMDp2bcyV48y2N2Pbit1brkGVGta0yd6VQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.3.tgz", - "integrity": "sha512-vSm/vOJe06pf14aGHfHl3Ar91Nlx4YYmohElDJ+17UbRwe99n987S/MhAlQOkONqf1utJor04ChkCPmKb8SWdw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@bramus/specificity": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", - "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-tree": "^3.0.0" - }, - "bin": { - "specificity": "bin/cli.js" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.1.tgz", - "integrity": "sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/@csstools/css-calc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", - "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.1.tgz", - "integrity": "sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^6.0.1", - "@csstools/css-calc": "^3.0.0" - }, - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.27", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.27.tgz", - "integrity": "sha512-sxP33Jwg1bviSUXAV43cVYdmjt2TLnLXNqCWl9xmxHawWVjGz/kEbdkr7F9pxJNBN2Mh+dq0crgItbW6tQvyow==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0" - }, - "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@exodus/bytes": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.14.1.tgz", - "integrity": "sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@noble/hashes": "^1.8.0 || ^2.0.0" - }, - "peerDependenciesMeta": { - "@noble/hashes": { - "optional": true - } - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@playwright/test": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", - "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", - "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", - "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", - "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", - "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", - "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", - "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", - "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", - "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", - "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", - "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", - "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", - "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", - "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", - "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", - "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", - "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", - "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", - "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", - "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", - "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", - "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", - "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", - "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", - "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", - "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@testing-library/dom": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", - "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "picocolors": "1.1.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@testing-library/jest-dom": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", - "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "picocolors": "^1.1.1", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/@testing-library/react": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", - "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0 || ^19.0.0", - "@types/react-dom": "^18.0.0 || ^19.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@testing-library/user-event": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz", - "integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.18.0" - } - }, - "node_modules/@types/prismjs": { - "version": "1.26.6", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", - "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/react-syntax-highlighter": { - "version": "15.5.13", - "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", - "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/@vitest/coverage-v8": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz", - "integrity": "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.7", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.12", - "magicast": "^0.3.5", - "std-env": "^3.8.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "2.1.9", - "vitest": "2.1.9" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", - "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", - "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", - "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "2.1.9", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", - "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", - "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.1.9", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "require-from-string": "^2.0.2" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001770", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", - "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-tree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", - "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.12.2", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.0.1.tgz", - "integrity": "sha512-IoJs7La+oFp/AB033wBStxNOJt4+9hHMxsXUPANcoXL2b3W4DZKghlJ2cI/eyeRZIQ9ysvYEorVhjrcYctWbog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^4.1.2", - "@csstools/css-syntax-patches-for-csstree": "^1.0.26", - "css-tree": "^3.1.0", - "lru-cache": "^11.2.5" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cssstyle/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/data-urls": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", - "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "dev": true, - "license": "MIT" - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dedent": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", - "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.302", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", - "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", - "license": "CC0-1.0" - }, - "node_modules/html-encoding-sniffer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@exodus/bytes": "^1.6.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-url-attributes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-config/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "28.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz", - "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@acemir/cssom": "^0.9.31", - "@asamuzakjp/dom-selector": "^6.8.1", - "@bramus/specificity": "^2.4.2", - "@exodus/bytes": "^1.11.0", - "cssstyle": "^6.0.1", - "data-urls": "^7.0.0", - "decimal.js": "^10.6.0", - "html-encoding-sniffer": "^6.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "parse5": "^8.0.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.0", - "undici": "^7.21.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^8.0.1", - "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", - "license": "MIT", - "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", - "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.4" - } - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/react-markdown": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", - "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "html-url-attributes": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" - } - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-syntax-highlighter": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz", - "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "highlight.js": "^10.4.1", - "highlightjs-vue": "^1.0.0", - "lowlight": "^1.17.0", - "prismjs": "^1.30.0", - "refractor": "^5.0.0" - }, - "engines": { - "node": ">= 16.20.2" - }, - "peerDependencies": { - "react": ">= 0.14.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/refractor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", - "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^9.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/rollup": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", - "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.1", - "@rollup/rollup-android-arm64": "4.57.1", - "@rollup/rollup-darwin-arm64": "4.57.1", - "@rollup/rollup-darwin-x64": "4.57.1", - "@rollup/rollup-freebsd-arm64": "4.57.1", - "@rollup/rollup-freebsd-x64": "4.57.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", - "@rollup/rollup-linux-arm-musleabihf": "4.57.1", - "@rollup/rollup-linux-arm64-gnu": "4.57.1", - "@rollup/rollup-linux-arm64-musl": "4.57.1", - "@rollup/rollup-linux-loong64-gnu": "4.57.1", - "@rollup/rollup-linux-loong64-musl": "4.57.1", - "@rollup/rollup-linux-ppc64-gnu": "4.57.1", - "@rollup/rollup-linux-ppc64-musl": "4.57.1", - "@rollup/rollup-linux-riscv64-gnu": "4.57.1", - "@rollup/rollup-linux-riscv64-musl": "4.57.1", - "@rollup/rollup-linux-s390x-gnu": "4.57.1", - "@rollup/rollup-linux-x64-gnu": "4.57.1", - "@rollup/rollup-linux-x64-musl": "4.57.1", - "@rollup/rollup-openbsd-x64": "4.57.1", - "@rollup/rollup-openharmony-arm64": "4.57.1", - "@rollup/rollup-win32-arm64-msvc": "4.57.1", - "@rollup/rollup-win32-ia32-msvc": "4.57.1", - "@rollup/rollup-win32-x64-gnu": "4.57.1", - "@rollup/rollup-win32-x64-msvc": "4.57.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tldts": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.23.tgz", - "integrity": "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^7.0.23" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.23.tgz", - "integrity": "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", - "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^7.0.5" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.3", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undici": { - "version": "7.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", - "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", - "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", - "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "2.1.9", - "@vitest/mocker": "2.1.9", - "@vitest/pretty-format": "^2.1.9", - "@vitest/runner": "2.1.9", - "@vitest/snapshot": "2.1.9", - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.9", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.9", - "@vitest/ui": "2.1.9", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/webidl-conversions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=20" - } - }, - "node_modules/whatwg-mimetype": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/whatwg-url": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", - "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@exodus/bytes": "^1.11.0", - "tr46": "^6.0.0", - "webidl-conversions": "^8.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/frontend/package.json b/frontend/package.json deleted file mode 100644 index 8ad6f87..0000000 --- a/frontend/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "living-spec-standalone", - "private": true, - "version": "0.4.1", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview", - "server": "cargo run --manifest-path server/Cargo.toml", - "test": "vitest run", - "test:unit": "vitest run", - "test:e2e": "playwright test", - "test:coverage": "vitest run --coverage" - }, - "dependencies": { - "@types/react-syntax-highlighter": "^15.5.13", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-markdown": "^10.1.0", - "react-syntax-highlighter": "^16.1.0" - }, - "devDependencies": { - "@biomejs/biome": "^2.4.2", - "@playwright/test": "^1.47.2", - "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^16.0.0", - "@testing-library/user-event": "^14.4.3", - "@types/node": "^25.0.0", - "@types/react": "^19.1.8", - "@types/react-dom": "^19.1.6", - "@vitejs/plugin-react": "^4.6.0", - "@vitest/coverage-v8": "^2.1.9", - "jest": "^29.0.0", - "jsdom": "^28.1.0", - "ts-jest": "^29.0.0", - "typescript": "~5.8.3", - "vite": "^5.4.21", - "vitest": "^2.1.4" - } -} diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts deleted file mode 100644 index 3204f58..0000000 --- a/frontend/playwright.config.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { defineConfig } from "@playwright/test"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const configDir = dirname(fileURLToPath(new URL(import.meta.url))); -const frontendRoot = resolve(configDir, "."); - -export default defineConfig({ - testDir: "./tests/e2e", - fullyParallel: true, - timeout: 30_000, - expect: { - timeout: 5_000, - }, - use: { - baseURL: "http://127.0.0.1:41700", - trace: "on-first-retry", - }, - webServer: { - command: - "pnpm exec vite --config vite.config.ts --host 127.0.0.1 --port 41700 --strictPort", - url: "http://127.0.0.1:41700/@vite/client", - reuseExistingServer: true, - timeout: 120_000, - cwd: frontendRoot, - }, -}); diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index d990c0c..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,238 +0,0 @@ -.logo.vite:hover { - filter: drop-shadow(0 0 2em #747bff); -} - -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafb); -} -:root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; - - color: #0f0f0f; - background-color: #f6f6f6; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -.container { - margin: 0; - padding-top: 0; - height: 100vh; - overflow: hidden; - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: 0.75s; -} - -.logo.tauri:hover { - filter: drop-shadow(0 0 2em #24c8db); -} - -.row { - display: flex; - justify-content: center; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} - -a:hover { - color: #535bf2; -} - -h1 { - text-align: center; -} - -input, -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; - transition: border-color 0.25s; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); -} - -button { - cursor: pointer; -} - -button:hover { - border-color: #396cd8; -} -button:active { - border-color: #396cd8; - background-color: #e8e8e8; -} - -input, -button { - outline: none; -} - -#greet-input { - margin-right: 5px; -} - -@media (prefers-color-scheme: dark) { - :root { - color: #f6f6f6; - background-color: #2f2f2f; - } - - a:hover { - color: #24c8db; - } - - input, - button { - color: #ffffff; - background-color: #0f0f0f98; - } - button:active { - background-color: #0f0f0f69; - } -} - -/* Collapsible tool output styling */ -details summary { - cursor: pointer; - user-select: none; -} - -details summary::-webkit-details-marker { - display: none; -} - -details[open] summary span:first-child { - transform: rotate(90deg); - display: inline-block; - transition: transform 0.2s ease; -} - -details summary span:first-child { - transition: transform 0.2s ease; -} - -/* Markdown body styling for dark theme */ -.markdown-body { - color: #ececec; - text-align: left; -} - -.markdown-body code { - background: #2f2f2f; - padding: 2px 6px; - border-radius: 3px; - font-family: monospace; -} - -.markdown-body pre { - background: #1a1a1a; - padding: 12px; - border-radius: 6px; - overflow-x: auto; - text-align: left; -} - -.markdown-body pre code { - background: transparent; - padding: 0; -} - -/* Syntax highlighter styling */ -.markdown-body div[class*="language-"] { - margin: 0; - border-radius: 6px; - text-align: left; -} - -.markdown-body pre[class*="language-"] { - margin: 0; - padding: 12px; - background: #1a1a1a; - text-align: left; -} - -/* Hide scroll bars globally while maintaining scroll functionality */ -/* Firefox */ -* { - scrollbar-width: none; -} - -/* Chrome, Safari, Edge */ -*::-webkit-scrollbar { - display: none; -} - -/* Ensure scroll functionality is maintained */ -html, -body, -#root { - height: 100%; - margin: 0; - overflow: hidden; -} - -/* Agent activity indicator pulse */ -@keyframes pulse { - 0%, - 100% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0.4; - transform: scale(0.85); - } -} - -/* Agent lozenge appearance animation (simulates arriving from agents panel) */ -@keyframes agentAppear { - from { - opacity: 0; - transform: translateY(-4px) scale(0.95); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -/* Thinking/loading pulse for text */ -.pulse { - animation: pulse 1.5s infinite; -} - -/* Agent entry fade-out for completed/failed agents */ -@keyframes agentFadeOut { - from { - opacity: 1; - } - to { - opacity: 0; - } -} diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx deleted file mode 100644 index 1ed7b03..0000000 --- a/frontend/src/App.test.tsx +++ /dev/null @@ -1,366 +0,0 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { api } from "./api/client"; - -vi.mock("./api/client", () => { - const api = { - getCurrentProject: vi.fn(), - getKnownProjects: vi.fn(), - getHomeDirectory: vi.fn(), - openProject: vi.fn(), - closeProject: vi.fn(), - forgetKnownProject: vi.fn(), - listDirectoryAbsolute: vi.fn(), - getOllamaModels: vi.fn(), - getAnthropicApiKeyExists: vi.fn(), - getAnthropicModels: vi.fn(), - getModelPreference: vi.fn(), - setModelPreference: vi.fn(), - cancelChat: vi.fn(), - setAnthropicApiKey: vi.fn(), - }; - class ChatWebSocket { - connect() {} - close() {} - sendChat() {} - cancel() {} - } - return { api, ChatWebSocket }; -}); - -vi.mock("./api/workflow", () => { - return { - workflowApi: { - getAcceptance: vi.fn().mockResolvedValue({ - can_accept: false, - reasons: [], - warning: null, - summary: { total: 0, passed: 0, failed: 0 }, - missing_categories: [], - }), - getReviewQueueAll: vi.fn().mockResolvedValue({ stories: [] }), - getUpcomingStories: vi.fn().mockResolvedValue({ stories: [] }), - recordTests: vi.fn(), - ensureAcceptance: vi.fn(), - getReviewQueue: vi.fn(), - collectCoverage: vi.fn(), - recordCoverage: vi.fn(), - getStoryTodos: vi.fn().mockResolvedValue({ stories: [] }), - }, - }; -}); - -const mockedApi = vi.mocked(api); - -describe("App", () => { - beforeEach(() => { - vi.resetModules(); - vi.clearAllMocks(); - mockedApi.getCurrentProject.mockResolvedValue(null); - mockedApi.getKnownProjects.mockResolvedValue([]); - mockedApi.getHomeDirectory.mockResolvedValue("/home/user"); - mockedApi.listDirectoryAbsolute.mockResolvedValue([]); - mockedApi.getOllamaModels.mockResolvedValue([]); - mockedApi.getAnthropicApiKeyExists.mockResolvedValue(false); - mockedApi.getAnthropicModels.mockResolvedValue([]); - mockedApi.getModelPreference.mockResolvedValue(null); - }); - - async function renderApp() { - const { default: App } = await import("./App"); - return render(); - } - - it("calls getCurrentProject() on mount", async () => { - await renderApp(); - - await waitFor(() => { - expect(mockedApi.getCurrentProject).toHaveBeenCalledTimes(1); - }); - }); - - it("skips selection screen and shows workspace when server already has a project open", async () => { - mockedApi.getCurrentProject.mockResolvedValue("/home/user/myproject"); - - await renderApp(); - - await waitFor(() => { - expect( - screen.queryByPlaceholderText(/\/path\/to\/project/i), - ).not.toBeInTheDocument(); - }); - }); - - it("renders the selection screen when no project is open", async () => { - await renderApp(); - - await waitFor(() => { - expect( - screen.getByPlaceholderText(/\/path\/to\/project/i), - ).toBeInTheDocument(); - }); - }); - - it("populates path input with home directory", async () => { - mockedApi.getHomeDirectory.mockResolvedValue("/Users/dave"); - - await renderApp(); - - await waitFor(() => { - const input = screen.getByPlaceholderText( - /\/path\/to\/project/i, - ) as HTMLInputElement; - expect(input.value).toBe("/Users/dave/"); - }); - }); - - it("opens project and shows chat view", async () => { - mockedApi.openProject.mockResolvedValue("/home/user/myproject"); - - await renderApp(); - - await waitFor(() => { - expect( - screen.getByPlaceholderText(/\/path\/to\/project/i), - ).toBeInTheDocument(); - }); - - const input = screen.getByPlaceholderText( - /\/path\/to\/project/i, - ) as HTMLInputElement; - await userEvent.clear(input); - await userEvent.type(input, "/home/user/myproject"); - - const openButton = screen.getByRole("button", { name: /open project/i }); - await userEvent.click(openButton); - - await waitFor(() => { - expect(mockedApi.openProject).toHaveBeenCalledWith( - "/home/user/myproject", - ); - }); - }); - - it("shows error when openProject fails", async () => { - mockedApi.openProject.mockRejectedValue(new Error("Path does not exist")); - - await renderApp(); - - await waitFor(() => { - expect( - screen.getByPlaceholderText(/\/path\/to\/project/i), - ).toBeInTheDocument(); - }); - - const input = screen.getByPlaceholderText( - /\/path\/to\/project/i, - ) as HTMLInputElement; - await userEvent.clear(input); - await userEvent.type(input, "/bad/path"); - - const openButton = screen.getByRole("button", { name: /open project/i }); - await userEvent.click(openButton); - - await waitFor(() => { - expect(screen.getByText(/Path does not exist/)).toBeInTheDocument(); - }); - }); - - it("shows known projects list", async () => { - mockedApi.getKnownProjects.mockResolvedValue([ - "/home/user/project1", - "/home/user/project2", - ]); - - await renderApp(); - - await waitFor(() => { - expect(screen.getByTitle("/home/user/project1")).toBeInTheDocument(); - expect(screen.getByTitle("/home/user/project2")).toBeInTheDocument(); - }); - }); - - it("shows error when path input is empty", async () => { - await renderApp(); - - await waitFor(() => { - expect( - screen.getByPlaceholderText(/\/path\/to\/project/i), - ).toBeInTheDocument(); - }); - - const input = screen.getByPlaceholderText( - /\/path\/to\/project/i, - ) as HTMLInputElement; - await userEvent.clear(input); - - const openButton = screen.getByRole("button", { name: /open project/i }); - await userEvent.click(openButton); - - await waitFor(() => { - expect( - screen.getByText(/Please enter a project path/i), - ).toBeInTheDocument(); - }); - }); - - it("calls forgetKnownProject and removes project from list", async () => { - mockedApi.getKnownProjects.mockResolvedValue(["/home/user/project1"]); - mockedApi.forgetKnownProject.mockResolvedValue(true); - - await renderApp(); - - await waitFor(() => { - expect(screen.getByTitle("/home/user/project1")).toBeInTheDocument(); - }); - - const forgetButton = screen.getByRole("button", { - name: /Forget project1/i, - }); - await userEvent.click(forgetButton); - - await waitFor(() => { - expect(mockedApi.forgetKnownProject).toHaveBeenCalledWith( - "/home/user/project1", - ); - expect( - screen.queryByTitle("/home/user/project1"), - ).not.toBeInTheDocument(); - }); - }); - - it("closes project and returns to selection screen", async () => { - mockedApi.openProject.mockResolvedValue("/home/user/myproject"); - mockedApi.closeProject.mockResolvedValue(true); - - await renderApp(); - - await waitFor(() => { - expect( - screen.getByPlaceholderText(/\/path\/to\/project/i), - ).toBeInTheDocument(); - }); - - const input = screen.getByPlaceholderText( - /\/path\/to\/project/i, - ) as HTMLInputElement; - await userEvent.clear(input); - await userEvent.type(input, "/home/user/myproject"); - - const openButton = screen.getByRole("button", { name: /open project/i }); - await userEvent.click(openButton); - - await waitFor(() => { - expect(mockedApi.openProject).toHaveBeenCalledWith( - "/home/user/myproject", - ); - }); - - // Chat view should appear with close button - const closeButton = await waitFor(() => screen.getByText("✕")); - await userEvent.click(closeButton); - - await waitFor(() => { - expect(mockedApi.closeProject).toHaveBeenCalled(); - expect( - screen.getByPlaceholderText(/\/path\/to\/project/i), - ).toBeInTheDocument(); - }); - }); - - it("handles ArrowDown and ArrowUp keyboard navigation when suggestions are visible", async () => { - mockedApi.listDirectoryAbsolute.mockResolvedValue([ - { name: "projects", kind: "dir" }, - { name: "documents", kind: "dir" }, - ]); - - await renderApp(); - - // Wait for suggestions to appear after debounce - await waitFor( - () => { - expect(screen.getByText(/projects\//)).toBeInTheDocument(); - }, - { timeout: 2000 }, - ); - - const input = screen.getByPlaceholderText(/\/path\/to\/project/i); - - // ArrowDown with matchList present — moves selection forward - fireEvent.keyDown(input, { key: "ArrowDown" }); - - // ArrowUp with matchList present — moves selection backward - fireEvent.keyDown(input, { key: "ArrowUp" }); - }); - - it("handles Tab keyboard navigation to accept suggestion", async () => { - mockedApi.listDirectoryAbsolute.mockResolvedValue([ - { name: "myrepo", kind: "dir" }, - ]); - - await renderApp(); - - await waitFor( - () => { - expect(screen.getByText(/myrepo\//)).toBeInTheDocument(); - }, - { timeout: 2000 }, - ); - - const input = screen.getByPlaceholderText(/\/path\/to\/project/i); - - // Tab with matchList present — accepts the selected match - fireEvent.keyDown(input, { key: "Tab" }); - }); - - it("handles Escape key to close suggestions", async () => { - mockedApi.listDirectoryAbsolute.mockResolvedValue([ - { name: "workspace", kind: "dir" }, - ]); - - await renderApp(); - - await waitFor( - () => { - expect(screen.getByText(/workspace\//)).toBeInTheDocument(); - }, - { timeout: 2000 }, - ); - - const input = screen.getByPlaceholderText(/\/path\/to\/project/i); - - // Escape closes suggestions - fireEvent.keyDown(input, { key: "Escape" }); - - await waitFor(() => { - expect(screen.queryByText(/workspace\//)).not.toBeInTheDocument(); - }); - }); - - it("handles Enter key to trigger project open", async () => { - mockedApi.openProject.mockResolvedValue("/home/user/myproject"); - - await renderApp(); - - await waitFor(() => { - expect( - screen.getByPlaceholderText(/\/path\/to\/project/i), - ).toBeInTheDocument(); - }); - - const input = screen.getByPlaceholderText( - /\/path\/to\/project/i, - ) as HTMLInputElement; - await userEvent.clear(input); - await userEvent.type(input, "/home/user/myproject"); - - fireEvent.keyDown(input, { key: "Enter" }); - - await waitFor(() => { - expect(mockedApi.openProject).toHaveBeenCalledWith( - "/home/user/myproject", - ); - }); - }); -}); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx deleted file mode 100644 index 316029a..0000000 --- a/frontend/src/App.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import * as React from "react"; -import { api } from "./api/client"; -import { Chat } from "./components/Chat"; -import { SelectionScreen } from "./components/selection/SelectionScreen"; -import { usePathCompletion } from "./components/selection/usePathCompletion"; -import "./App.css"; - -function App() { - const [projectPath, setProjectPath] = React.useState(null); - const [_view, setView] = React.useState<"chat" | "token-usage">("chat"); - const [isCheckingProject, setIsCheckingProject] = React.useState(true); - const [errorMsg, setErrorMsg] = React.useState(null); - const [pathInput, setPathInput] = React.useState(""); - const [isOpening, setIsOpening] = React.useState(false); - const [knownProjects, setKnownProjects] = React.useState([]); - const [homeDir, setHomeDir] = React.useState(null); - - React.useEffect(() => { - api - .getCurrentProject() - .then((path) => { - if (path) { - setProjectPath(path); - } - }) - .catch((error) => console.error(error)) - .finally(() => { - setIsCheckingProject(false); - }); - }, []); - - React.useEffect(() => { - api - .getKnownProjects() - .then((projects) => setKnownProjects(projects)) - .catch((error) => console.error(error)); - }, []); - - React.useEffect(() => { - let active = true; - api - .getHomeDirectory() - .then((home) => { - if (!active) return; - setHomeDir(home); - setPathInput((current) => { - if (current.trim()) { - return current; - } - const initial = home.endsWith("/") ? home : `${home}/`; - return initial; - }); - }) - .catch((error) => { - console.error(error); - }); - - return () => { - active = false; - }; - }, []); - - const { - matchList, - selectedMatch, - suggestionTail, - completionError, - currentPartial, - setSelectedMatch, - acceptSelectedMatch, - acceptMatch, - closeSuggestions, - } = usePathCompletion({ - pathInput, - setPathInput, - homeDir, - listDirectoryAbsolute: api.listDirectoryAbsolute, - }); - - async function openProject(path: string) { - const trimmedPath = path.trim(); - if (!trimmedPath) { - setErrorMsg("Please enter a project path."); - return; - } - - try { - setErrorMsg(null); - setIsOpening(true); - const confirmedPath = await api.openProject(trimmedPath); - setProjectPath(confirmedPath); - } catch (e) { - console.error(e); - const message = - e instanceof Error - ? e.message - : typeof e === "string" - ? e - : "An error occurred opening the project."; - - setErrorMsg(message); - } finally { - setIsOpening(false); - } - } - - function handleOpen() { - void openProject(pathInput); - } - - async function handleForgetProject(path: string) { - try { - await api.forgetKnownProject(path); - setKnownProjects((prev) => prev.filter((p) => p !== path)); - } catch (error) { - console.error(error); - } - } - - async function closeProject() { - try { - await api.closeProject(); - setProjectPath(null); - setView("chat"); - } catch (e) { - console.error(e); - } - } - - function handlePathInputKeyDown( - event: React.KeyboardEvent, - ) { - if (event.key === "ArrowDown") { - if (matchList.length > 0) { - event.preventDefault(); - setSelectedMatch((selectedMatch + 1) % matchList.length); - } - } else if (event.key === "ArrowUp") { - if (matchList.length > 0) { - event.preventDefault(); - setSelectedMatch( - (selectedMatch - 1 + matchList.length) % matchList.length, - ); - } - } else if (event.key === "Tab") { - if (matchList.length > 0) { - event.preventDefault(); - acceptSelectedMatch(); - } - } else if (event.key === "Escape") { - event.preventDefault(); - closeSuggestions(); - } else if (event.key === "Enter") { - handleOpen(); - } - } - - if (isCheckingProject) { - return null; - } - - return ( -
- {!projectPath ? ( - - ) : ( -
- -
- )} - - {errorMsg && ( -
-

Error: {errorMsg}

-
- )} -
- ); -} - -export default App; diff --git a/frontend/src/api/agents.test.ts b/frontend/src/api/agents.test.ts deleted file mode 100644 index ca86f6a..0000000 --- a/frontend/src/api/agents.test.ts +++ /dev/null @@ -1,387 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import type { AgentConfigInfo, AgentEvent, AgentInfo } from "./agents"; -import { agentsApi, subscribeAgentStream } from "./agents"; - -const mockFetch = vi.fn(); - -beforeEach(() => { - vi.stubGlobal("fetch", mockFetch); -}); - -afterEach(() => { - vi.restoreAllMocks(); -}); - -function okResponse(body: unknown) { - return new Response(JSON.stringify(body), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); -} - -function errorResponse(status: number, text: string) { - return new Response(text, { status }); -} - -const sampleAgent: AgentInfo = { - story_id: "42_story_test", - agent_name: "coder", - status: "running", - session_id: null, - worktree_path: null, - base_branch: null, - log_session_id: null, -}; - -const sampleConfig: AgentConfigInfo = { - name: "coder", - role: "engineer", - stage: "coder", - model: "claude-sonnet-4-6", - allowed_tools: null, - max_turns: null, - max_budget_usd: null, -}; - -// ── agentsApi ──────────────────────────────────────────────────────────────── - -describe("agentsApi", () => { - describe("startAgent", () => { - it("sends POST to /agents/start with story_id", async () => { - mockFetch.mockResolvedValueOnce(okResponse(sampleAgent)); - - const result = await agentsApi.startAgent("42_story_test"); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/agents/start", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ - story_id: "42_story_test", - agent_name: undefined, - }), - }), - ); - expect(result).toEqual(sampleAgent); - }); - - it("sends POST with optional agent_name", async () => { - mockFetch.mockResolvedValueOnce(okResponse(sampleAgent)); - - await agentsApi.startAgent("42_story_test", "coder"); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/agents/start", - expect.objectContaining({ - body: JSON.stringify({ - story_id: "42_story_test", - agent_name: "coder", - }), - }), - ); - }); - - it("uses custom baseUrl when provided", async () => { - mockFetch.mockResolvedValueOnce(okResponse(sampleAgent)); - - await agentsApi.startAgent( - "42_story_test", - undefined, - "http://localhost:3002/api", - ); - - expect(mockFetch).toHaveBeenCalledWith( - "http://localhost:3002/api/agents/start", - expect.objectContaining({ method: "POST" }), - ); - }); - }); - - describe("stopAgent", () => { - it("sends POST to /agents/stop with story_id and agent_name", async () => { - mockFetch.mockResolvedValueOnce(okResponse(true)); - - const result = await agentsApi.stopAgent("42_story_test", "coder"); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/agents/stop", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ - story_id: "42_story_test", - agent_name: "coder", - }), - }), - ); - expect(result).toBe(true); - }); - - it("uses custom baseUrl when provided", async () => { - mockFetch.mockResolvedValueOnce(okResponse(false)); - - await agentsApi.stopAgent( - "42_story_test", - "coder", - "http://localhost:3002/api", - ); - - expect(mockFetch).toHaveBeenCalledWith( - "http://localhost:3002/api/agents/stop", - expect.objectContaining({ method: "POST" }), - ); - }); - }); - - describe("listAgents", () => { - it("sends GET to /agents and returns agent list", async () => { - mockFetch.mockResolvedValueOnce(okResponse([sampleAgent])); - - const result = await agentsApi.listAgents(); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/agents", - expect.objectContaining({}), - ); - expect(result).toEqual([sampleAgent]); - }); - - it("returns empty array when no agents running", async () => { - mockFetch.mockResolvedValueOnce(okResponse([])); - - const result = await agentsApi.listAgents(); - expect(result).toEqual([]); - }); - - it("uses custom baseUrl when provided", async () => { - mockFetch.mockResolvedValueOnce(okResponse([])); - - await agentsApi.listAgents("http://localhost:3002/api"); - - expect(mockFetch).toHaveBeenCalledWith( - "http://localhost:3002/api/agents", - expect.objectContaining({}), - ); - }); - }); - - describe("getAgentConfig", () => { - it("sends GET to /agents/config and returns config list", async () => { - mockFetch.mockResolvedValueOnce(okResponse([sampleConfig])); - - const result = await agentsApi.getAgentConfig(); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/agents/config", - expect.objectContaining({}), - ); - expect(result).toEqual([sampleConfig]); - }); - - it("uses custom baseUrl when provided", async () => { - mockFetch.mockResolvedValueOnce(okResponse([sampleConfig])); - - await agentsApi.getAgentConfig("http://localhost:3002/api"); - - expect(mockFetch).toHaveBeenCalledWith( - "http://localhost:3002/api/agents/config", - expect.objectContaining({}), - ); - }); - }); - - describe("reloadConfig", () => { - it("sends POST to /agents/config/reload", async () => { - mockFetch.mockResolvedValueOnce(okResponse([sampleConfig])); - - const result = await agentsApi.reloadConfig(); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/agents/config/reload", - expect.objectContaining({ method: "POST" }), - ); - expect(result).toEqual([sampleConfig]); - }); - - it("uses custom baseUrl when provided", async () => { - mockFetch.mockResolvedValueOnce(okResponse([])); - - await agentsApi.reloadConfig("http://localhost:3002/api"); - - expect(mockFetch).toHaveBeenCalledWith( - "http://localhost:3002/api/agents/config/reload", - expect.objectContaining({ method: "POST" }), - ); - }); - }); - - describe("error handling", () => { - it("throws on non-ok response with body text", async () => { - mockFetch.mockResolvedValueOnce(errorResponse(404, "agent not found")); - - await expect(agentsApi.listAgents()).rejects.toThrow("agent not found"); - }); - - it("throws with status code when no body", async () => { - mockFetch.mockResolvedValueOnce(errorResponse(500, "")); - - await expect(agentsApi.listAgents()).rejects.toThrow( - "Request failed (500)", - ); - }); - }); -}); - -// ── subscribeAgentStream ───────────────────────────────────────────────────── - -interface MockESInstance { - url: string; - onmessage: ((e: { data: string }) => void) | null; - onerror: ((e: Event) => void) | null; - close: ReturnType; - simulateMessage: (data: unknown) => void; - simulateError: (e: Event) => void; -} - -function makeMockEventSource() { - const instances: MockESInstance[] = []; - - class MockEventSource { - onmessage: ((e: { data: string }) => void) | null = null; - onerror: ((e: Event) => void) | null = null; - close = vi.fn(); - - constructor(public url: string) { - instances.push(this as unknown as MockESInstance); - } - - simulateMessage(data: unknown) { - this.onmessage?.({ data: JSON.stringify(data) }); - } - - simulateError(e: Event) { - this.onerror?.(e); - } - } - - return { MockEventSource, instances }; -} - -describe("subscribeAgentStream", () => { - let instances: MockESInstance[]; - - beforeEach(() => { - const { MockEventSource, instances: inst } = makeMockEventSource(); - instances = inst; - vi.stubGlobal("EventSource", MockEventSource); - }); - - it("creates an EventSource with encoded story and agent in URL", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - expect(instances).toHaveLength(1); - expect(instances[0].url).toContain( - `/agents/${encodeURIComponent("42_story_test")}/${encodeURIComponent("coder")}/stream`, - ); - }); - - it("calls onEvent when a message is received", () => { - const onEvent = vi.fn(); - subscribeAgentStream("42_story_test", "coder", onEvent); - - const event: AgentEvent = { type: "output", text: "hello" }; - instances[0].simulateMessage(event); - - expect(onEvent).toHaveBeenCalledWith(event); - }); - - it("closes EventSource on 'done' type event", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - instances[0].simulateMessage({ type: "done" }); - - expect(instances[0].close).toHaveBeenCalled(); - }); - - it("closes EventSource on 'error' type event", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - instances[0].simulateMessage({ - type: "error", - message: "something failed", - }); - - expect(instances[0].close).toHaveBeenCalled(); - }); - - it("closes EventSource on status=stopped event", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - instances[0].simulateMessage({ type: "status", status: "stopped" }); - - expect(instances[0].close).toHaveBeenCalled(); - }); - - it("does not close on status=running event", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - instances[0].simulateMessage({ type: "status", status: "running" }); - - expect(instances[0].close).not.toHaveBeenCalled(); - }); - - it("does not close on 'output' event", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - instances[0].simulateMessage({ type: "output", text: "building..." }); - - expect(instances[0].close).not.toHaveBeenCalled(); - }); - - it("calls onError and closes on EventSource onerror", () => { - const onError = vi.fn(); - subscribeAgentStream("42_story_test", "coder", vi.fn(), onError); - - const err = new Event("error"); - instances[0].simulateError(err); - - expect(onError).toHaveBeenCalledWith(err); - expect(instances[0].close).toHaveBeenCalled(); - }); - - it("closes EventSource when onError is not provided", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - const err = new Event("error"); - instances[0].simulateError(err); - - expect(instances[0].close).toHaveBeenCalled(); - }); - - it("closes EventSource when cleanup function is called", () => { - const cleanup = subscribeAgentStream("42_story_test", "coder", vi.fn()); - - cleanup(); - - expect(instances[0].close).toHaveBeenCalled(); - }); - - it("handles malformed JSON without throwing", () => { - subscribeAgentStream("42_story_test", "coder", vi.fn()); - - expect(() => { - instances[0].onmessage?.({ data: "{ not valid json" }); - }).not.toThrow(); - }); - - it("delivers multiple events before a terminal event", () => { - const onEvent = vi.fn(); - subscribeAgentStream("42_story_test", "coder", onEvent); - - instances[0].simulateMessage({ type: "output", text: "line 1" }); - instances[0].simulateMessage({ type: "output", text: "line 2" }); - instances[0].simulateMessage({ type: "done" }); - - expect(onEvent).toHaveBeenCalledTimes(3); - expect(instances[0].close).toHaveBeenCalledTimes(1); - }); -}); diff --git a/frontend/src/api/agents.ts b/frontend/src/api/agents.ts deleted file mode 100644 index ca1a139..0000000 --- a/frontend/src/api/agents.ts +++ /dev/null @@ -1,162 +0,0 @@ -export type AgentStatusValue = "pending" | "running" | "completed" | "failed"; - -export interface AgentInfo { - story_id: string; - agent_name: string; - status: AgentStatusValue; - session_id: string | null; - worktree_path: string | null; - base_branch: string | null; - log_session_id: string | null; -} - -export interface AgentEvent { - type: - | "status" - | "output" - | "thinking" - | "agent_json" - | "done" - | "error" - | "warning"; - story_id?: string; - agent_name?: string; - status?: string; - text?: string; - data?: unknown; - session_id?: string | null; - message?: string; -} - -export interface AgentConfigInfo { - name: string; - role: string; - stage: string | null; - model: string | null; - allowed_tools: string[] | null; - max_turns: number | null; - max_budget_usd: number | null; -} - -const DEFAULT_API_BASE = "/api"; - -function buildApiUrl(path: string, baseUrl = DEFAULT_API_BASE): string { - return `${baseUrl}${path}`; -} - -async function requestJson( - path: string, - options: RequestInit = {}, - baseUrl = DEFAULT_API_BASE, -): Promise { - const res = await fetch(buildApiUrl(path, baseUrl), { - headers: { - "Content-Type": "application/json", - ...(options.headers ?? {}), - }, - ...options, - }); - - if (!res.ok) { - const text = await res.text(); - throw new Error(text || `Request failed (${res.status})`); - } - - return res.json() as Promise; -} - -export const agentsApi = { - startAgent(storyId: string, agentName?: string, baseUrl?: string) { - return requestJson( - "/agents/start", - { - method: "POST", - body: JSON.stringify({ - story_id: storyId, - agent_name: agentName, - }), - }, - baseUrl, - ); - }, - - stopAgent(storyId: string, agentName: string, baseUrl?: string) { - return requestJson( - "/agents/stop", - { - method: "POST", - body: JSON.stringify({ - story_id: storyId, - agent_name: agentName, - }), - }, - baseUrl, - ); - }, - - listAgents(baseUrl?: string) { - return requestJson("/agents", {}, baseUrl); - }, - - getAgentConfig(baseUrl?: string) { - return requestJson("/agents/config", {}, baseUrl); - }, - - reloadConfig(baseUrl?: string) { - return requestJson( - "/agents/config/reload", - { method: "POST" }, - baseUrl, - ); - }, - - getAgentOutput(storyId: string, agentName: string, baseUrl?: string) { - return requestJson<{ output: string }>( - `/agents/${encodeURIComponent(storyId)}/${encodeURIComponent(agentName)}/output`, - {}, - baseUrl, - ); - }, -}; - -/** - * Subscribe to SSE events for a running agent. - * Returns a cleanup function to close the connection. - */ -export function subscribeAgentStream( - storyId: string, - agentName: string, - onEvent: (event: AgentEvent) => void, - onError?: (error: Event) => void, -): () => void { - const url = `/agents/${encodeURIComponent(storyId)}/${encodeURIComponent(agentName)}/stream`; - - const eventSource = new EventSource(url); - - eventSource.onmessage = (e) => { - try { - const data = JSON.parse(e.data) as AgentEvent; - onEvent(data); - - // Close on terminal events - if ( - data.type === "done" || - data.type === "error" || - (data.type === "status" && data.status === "stopped") - ) { - eventSource.close(); - } - } catch (err) { - console.error("Failed to parse agent event:", err); - } - }; - - eventSource.onerror = (e) => { - onError?.(e); - eventSource.close(); - }; - - return () => { - eventSource.close(); - }; -} diff --git a/frontend/src/api/client.test.ts b/frontend/src/api/client.test.ts deleted file mode 100644 index 9c03cab..0000000 --- a/frontend/src/api/client.test.ts +++ /dev/null @@ -1,433 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { api, ChatWebSocket, resolveWsHost } from "./client"; - -const mockFetch = vi.fn(); - -beforeEach(() => { - vi.stubGlobal("fetch", mockFetch); -}); - -afterEach(() => { - vi.restoreAllMocks(); -}); - -function okResponse(body: unknown) { - return new Response(JSON.stringify(body), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); -} - -function errorResponse(status: number, text: string) { - return new Response(text, { status }); -} - -describe("api client", () => { - describe("getCurrentProject", () => { - it("sends GET to /project", async () => { - mockFetch.mockResolvedValueOnce(okResponse("/home/user/project")); - - const result = await api.getCurrentProject(); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/project", - expect.objectContaining({}), - ); - expect(result).toBe("/home/user/project"); - }); - - it("returns null when no project open", async () => { - mockFetch.mockResolvedValueOnce(okResponse(null)); - - const result = await api.getCurrentProject(); - expect(result).toBeNull(); - }); - }); - - describe("openProject", () => { - it("sends POST with path", async () => { - mockFetch.mockResolvedValueOnce(okResponse("/home/user/project")); - - await api.openProject("/home/user/project"); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/project", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ path: "/home/user/project" }), - }), - ); - }); - }); - - describe("closeProject", () => { - it("sends DELETE to /project", async () => { - mockFetch.mockResolvedValueOnce(okResponse(true)); - - await api.closeProject(); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/project", - expect.objectContaining({ method: "DELETE" }), - ); - }); - }); - - describe("getKnownProjects", () => { - it("returns array of project paths", async () => { - mockFetch.mockResolvedValueOnce(okResponse(["/a", "/b"])); - - const result = await api.getKnownProjects(); - expect(result).toEqual(["/a", "/b"]); - }); - }); - - describe("error handling", () => { - it("throws on non-ok response with body text", async () => { - mockFetch.mockResolvedValueOnce(errorResponse(404, "Not found")); - - await expect(api.getCurrentProject()).rejects.toThrow("Not found"); - }); - - it("throws with status code when no body", async () => { - mockFetch.mockResolvedValueOnce(errorResponse(500, "")); - - await expect(api.getCurrentProject()).rejects.toThrow( - "Request failed (500)", - ); - }); - }); - - describe("searchFiles", () => { - it("sends POST with query", async () => { - mockFetch.mockResolvedValueOnce( - okResponse([{ path: "src/main.rs", matches: 1 }]), - ); - - const result = await api.searchFiles("hello"); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/fs/search", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ query: "hello" }), - }), - ); - expect(result).toHaveLength(1); - }); - }); - - describe("execShell", () => { - it("sends POST with command and args", async () => { - mockFetch.mockResolvedValueOnce( - okResponse({ stdout: "output", stderr: "", exit_code: 0 }), - ); - - const result = await api.execShell("ls", ["-la"]); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/shell/exec", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ command: "ls", args: ["-la"] }), - }), - ); - expect(result.exit_code).toBe(0); - }); - }); - - describe("resolveWsHost", () => { - it("uses env port in dev mode", () => { - expect(resolveWsHost(true, "4200", "example.com")).toBe("127.0.0.1:4200"); - }); - - it("defaults to 3001 in dev mode when no env port", () => { - expect(resolveWsHost(true, undefined, "example.com")).toBe( - "127.0.0.1:3001", - ); - }); - - it("uses location host in production", () => { - expect(resolveWsHost(false, "4200", "myapp.com:8080")).toBe( - "myapp.com:8080", - ); - }); - }); -}); - -// ── ChatWebSocket reconnect tests ─────────────────────────────────────────── - -interface MockWsInstance { - onopen: (() => void) | null; - onclose: (() => void) | null; - onmessage: ((e: { data: string }) => void) | null; - onerror: (() => void) | null; - readyState: number; - sentMessages: string[]; - send: (data: string) => void; - close: () => void; - simulateClose: () => void; - simulateMessage: (data: Record) => void; -} - -function makeMockWebSocket() { - const instances: MockWsInstance[] = []; - - class MockWebSocket { - static readonly CONNECTING = 0; - static readonly OPEN = 1; - static readonly CLOSING = 2; - static readonly CLOSED = 3; - - onopen: (() => void) | null = null; - onclose: (() => void) | null = null; - onmessage: ((e: { data: string }) => void) | null = null; - onerror: (() => void) | null = null; - readyState = 0; - sentMessages: string[] = []; - - constructor(_url: string) { - instances.push(this as unknown as MockWsInstance); - } - - send(data: string) { - this.sentMessages.push(data); - } - - close() { - this.readyState = 3; - this.onclose?.(); - } - - simulateClose() { - this.readyState = 3; - this.onclose?.(); - } - - simulateMessage(data: Record) { - this.onmessage?.({ data: JSON.stringify(data) }); - } - } - - return { MockWebSocket, instances }; -} - -describe("ChatWebSocket", () => { - beforeEach(() => { - vi.useFakeTimers(); - const { MockWebSocket } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - // Reset shared static state between tests - (ChatWebSocket as unknown as { sharedSocket: null }).sharedSocket = null; - (ChatWebSocket as unknown as { refCount: number }).refCount = 0; - }); - - afterEach(() => { - vi.useRealTimers(); - vi.restoreAllMocks(); - }); - - it("schedules reconnect after socket closes unexpectedly", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - - expect(instances).toHaveLength(1); - - instances[0].simulateClose(); - - // No new socket created yet - expect(instances).toHaveLength(1); - - // Advance past the initial 1s reconnect delay - vi.advanceTimersByTime(1001); - - // A new socket should now have been created - expect(instances).toHaveLength(2); - }); - - it("delivers pipeline_state after reconnect", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const onPipelineState = vi.fn(); - const ws = new ChatWebSocket(); - ws.connect({ onPipelineState }); - - // Simulate server restart - instances[0].simulateClose(); - vi.advanceTimersByTime(1001); - - // Server pushes pipeline_state on fresh connection - const freshState = { - backlog: [{ story_id: "1_story_test", name: "Test", error: null }], - current: [], - qa: [], - merge: [], - done: [], - }; - instances[1].simulateMessage({ type: "pipeline_state", ...freshState }); - - expect(onPipelineState).toHaveBeenCalledWith(freshState); - }); - - it("does not reconnect after explicit close()", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - - // Explicit close disables reconnect - ws.close(); - - // Advance through both the DEV close-defer (250ms) and reconnect window - vi.advanceTimersByTime(2000); - - // No new socket should be created - expect(instances).toHaveLength(1); - }); - - it("uses exponential backoff on repeated failures", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - - // First close → reconnects after 1s - instances[0].simulateClose(); - vi.advanceTimersByTime(1001); - expect(instances).toHaveLength(2); - - // Second close → reconnects after 2s (doubled) - instances[1].simulateClose(); - vi.advanceTimersByTime(1500); - // Not yet (delay is now 2s) - expect(instances).toHaveLength(2); - vi.advanceTimersByTime(600); - expect(instances).toHaveLength(3); - }); - - it("resets reconnect delay after successful open", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - - // Disconnect and reconnect twice to raise the delay - instances[0].simulateClose(); - vi.advanceTimersByTime(1001); - - instances[1].simulateClose(); - vi.advanceTimersByTime(2001); - - // Simulate a successful open on third socket — resets delay to 1s - instances[2].onopen?.(); - - // Close again — should use the reset 1s delay - instances[2].simulateClose(); - vi.advanceTimersByTime(1001); - - expect(instances).toHaveLength(4); - }); -}); - -describe("ChatWebSocket heartbeat", () => { - beforeEach(() => { - vi.useFakeTimers(); - const { MockWebSocket } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - (ChatWebSocket as unknown as { sharedSocket: null }).sharedSocket = null; - (ChatWebSocket as unknown as { refCount: number }).refCount = 0; - }); - - afterEach(() => { - vi.useRealTimers(); - vi.restoreAllMocks(); - }); - - it("sends ping after heartbeat interval", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - instances[0].readyState = 1; // OPEN - instances[0].onopen?.(); // starts heartbeat - - vi.advanceTimersByTime(29_999); - expect(instances[0].sentMessages).toHaveLength(0); - - vi.advanceTimersByTime(1); - expect(instances[0].sentMessages).toHaveLength(1); - expect(JSON.parse(instances[0].sentMessages[0])).toEqual({ type: "ping" }); - - ws.close(); - }); - - it("closes stale connection when pong is not received", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - instances[0].readyState = 1; // OPEN - instances[0].onopen?.(); // starts heartbeat - - // Fire heartbeat — sends ping and starts pong timeout - vi.advanceTimersByTime(30_000); - - // No pong received; advance past pong timeout → socket closed → reconnect scheduled - vi.advanceTimersByTime(5_000); - - // Advance past reconnect delay - vi.advanceTimersByTime(1_001); - - expect(instances).toHaveLength(2); - ws.close(); - }); - - it("does not close when pong is received before timeout", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - instances[0].readyState = 1; // OPEN - instances[0].onopen?.(); // starts heartbeat - - // Fire heartbeat - vi.advanceTimersByTime(30_000); - - // Server responds with pong — clears the pong timeout - instances[0].simulateMessage({ type: "pong" }); - - // Advance past where pong timeout would have fired - vi.advanceTimersByTime(5_001); - - // No reconnect triggered - expect(instances).toHaveLength(1); - ws.close(); - }); - - it("stops sending pings after explicit close", () => { - const { MockWebSocket, instances } = makeMockWebSocket(); - vi.stubGlobal("WebSocket", MockWebSocket); - - const ws = new ChatWebSocket(); - ws.connect({}); - instances[0].readyState = 1; // OPEN - instances[0].onopen?.(); // starts heartbeat - - ws.close(); - - // Advance well past multiple heartbeat intervals - vi.advanceTimersByTime(90_000); - - expect(instances[0].sentMessages).toHaveLength(0); - }); -}); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts deleted file mode 100644 index 6d682e9..0000000 --- a/frontend/src/api/client.ts +++ /dev/null @@ -1,699 +0,0 @@ -export type WsRequest = - | { - type: "chat"; - messages: Message[]; - config: ProviderConfig; - } - | { - type: "cancel"; - } - | { - type: "permission_response"; - request_id: string; - approved: boolean; - always_allow: boolean; - } - | { type: "ping" } - | { - type: "side_question"; - question: string; - context_messages: Message[]; - config: ProviderConfig; - }; - -export interface AgentAssignment { - agent_name: string; - model: string | null; - status: string; -} - -export interface PipelineStageItem { - story_id: string; - name: string | null; - error: string | null; - merge_failure: string | null; - agent: AgentAssignment | null; - review_hold: boolean | null; - qa: string | null; -} - -export interface PipelineState { - backlog: PipelineStageItem[]; - current: PipelineStageItem[]; - qa: PipelineStageItem[]; - merge: PipelineStageItem[]; - done: PipelineStageItem[]; -} - -export type WsResponse = - | { type: "token"; content: string } - | { type: "update"; messages: Message[] } - | { type: "session_id"; session_id: string } - | { type: "error"; message: string } - | { - type: "pipeline_state"; - backlog: PipelineStageItem[]; - current: PipelineStageItem[]; - qa: PipelineStageItem[]; - merge: PipelineStageItem[]; - done: PipelineStageItem[]; - } - | { - type: "permission_request"; - request_id: string; - tool_name: string; - tool_input: Record; - } - | { type: "tool_activity"; tool_name: string } - | { - type: "reconciliation_progress"; - story_id: string; - status: string; - message: string; - } - /** `.story_kit/project.toml` was modified; re-fetch the agent roster. */ - | { type: "agent_config_changed" } - /** An agent started, stopped, or changed state; re-fetch agent list. */ - | { type: "agent_state_changed" } - | { type: "tool_activity"; tool_name: string } - /** Heartbeat response confirming the connection is alive. */ - | { type: "pong" } - /** Sent on connect when the project still needs onboarding (specs are placeholders). */ - | { type: "onboarding_status"; needs_onboarding: boolean } - /** Streaming thinking token from an extended-thinking block, separate from regular text. */ - | { type: "thinking_token"; content: string } - /** Streaming token from a /btw side question response. */ - | { type: "side_question_token"; content: string } - /** Final signal that the /btw side question has been fully answered. */ - | { type: "side_question_done"; response: string } - /** A single server log entry (bulk on connect, then live). */ - | { type: "log_entry"; timestamp: string; level: string; message: string }; - -export interface ProviderConfig { - provider: string; - model: string; - base_url?: string; - enable_tools?: boolean; - session_id?: string; -} - -export type Role = "system" | "user" | "assistant" | "tool"; - -export interface ToolCall { - id?: string; - type: string; - function: { - name: string; - arguments: string; - }; -} - -export interface Message { - role: Role; - content: string; - tool_calls?: ToolCall[]; - tool_call_id?: string; -} - -export interface AnthropicModelInfo { - id: string; - context_window: number; -} - -export interface WorkItemContent { - content: string; - stage: string; - name: string | null; - agent: string | null; -} - -export interface TestCaseResult { - name: string; - status: "pass" | "fail"; - details: string | null; -} - -export interface TestResultsResponse { - unit: TestCaseResult[]; - integration: TestCaseResult[]; -} - -export interface FileEntry { - name: string; - kind: "file" | "dir"; -} - -export interface SearchResult { - path: string; - matches: number; -} - -export interface AgentCostEntry { - agent_name: string; - model: string | null; - input_tokens: number; - output_tokens: number; - cache_creation_input_tokens: number; - cache_read_input_tokens: number; - total_cost_usd: number; -} - -export interface TokenCostResponse { - total_cost_usd: number; - agents: AgentCostEntry[]; -} - -export interface TokenUsageRecord { - story_id: string; - agent_name: string; - model: string | null; - timestamp: string; - input_tokens: number; - output_tokens: number; - cache_creation_input_tokens: number; - cache_read_input_tokens: number; - total_cost_usd: number; -} - -export interface AllTokenUsageResponse { - records: TokenUsageRecord[]; -} - -export interface CommandOutput { - stdout: string; - stderr: string; - exit_code: number; -} - -declare const __STORKIT_PORT__: string; - -const DEFAULT_API_BASE = "/api"; -const DEFAULT_WS_PATH = "/ws"; - -export function resolveWsHost( - isDev: boolean, - envPort: string | undefined, - locationHost: string, -): string { - return isDev ? `127.0.0.1:${envPort || "3001"}` : locationHost; -} - -function buildApiUrl(path: string, baseUrl = DEFAULT_API_BASE): string { - return `${baseUrl}${path}`; -} - -async function requestJson( - path: string, - options: RequestInit = {}, - baseUrl = DEFAULT_API_BASE, -): Promise { - const res = await fetch(buildApiUrl(path, baseUrl), { - headers: { - "Content-Type": "application/json", - ...(options.headers ?? {}), - }, - ...options, - }); - - if (!res.ok) { - const text = await res.text(); - throw new Error(text || `Request failed (${res.status})`); - } - - return res.json() as Promise; -} - -export const api = { - getCurrentProject(baseUrl?: string) { - return requestJson("/project", {}, baseUrl); - }, - getKnownProjects(baseUrl?: string) { - return requestJson("/projects", {}, baseUrl); - }, - forgetKnownProject(path: string, baseUrl?: string) { - return requestJson( - "/projects/forget", - { method: "POST", body: JSON.stringify({ path }) }, - baseUrl, - ); - }, - openProject(path: string, baseUrl?: string) { - return requestJson( - "/project", - { method: "POST", body: JSON.stringify({ path }) }, - baseUrl, - ); - }, - closeProject(baseUrl?: string) { - return requestJson("/project", { method: "DELETE" }, baseUrl); - }, - getModelPreference(baseUrl?: string) { - return requestJson("/model", {}, baseUrl); - }, - setModelPreference(model: string, baseUrl?: string) { - return requestJson( - "/model", - { method: "POST", body: JSON.stringify({ model }) }, - baseUrl, - ); - }, - getOllamaModels(baseUrlParam?: string, baseUrl?: string) { - const url = new URL( - buildApiUrl("/ollama/models", baseUrl), - window.location.origin, - ); - if (baseUrlParam) { - url.searchParams.set("base_url", baseUrlParam); - } - return requestJson(url.pathname + url.search, {}, ""); - }, - getAnthropicApiKeyExists(baseUrl?: string) { - return requestJson("/anthropic/key/exists", {}, baseUrl); - }, - getAnthropicModels(baseUrl?: string) { - return requestJson("/anthropic/models", {}, baseUrl); - }, - setAnthropicApiKey(api_key: string, baseUrl?: string) { - return requestJson( - "/anthropic/key", - { method: "POST", body: JSON.stringify({ api_key }) }, - baseUrl, - ); - }, - readFile(path: string, baseUrl?: string) { - return requestJson( - "/fs/read", - { method: "POST", body: JSON.stringify({ path }) }, - baseUrl, - ); - }, - writeFile(path: string, content: string, baseUrl?: string) { - return requestJson( - "/fs/write", - { method: "POST", body: JSON.stringify({ path, content }) }, - baseUrl, - ); - }, - listDirectory(path: string, baseUrl?: string) { - return requestJson( - "/fs/list", - { method: "POST", body: JSON.stringify({ path }) }, - baseUrl, - ); - }, - listDirectoryAbsolute(path: string, baseUrl?: string) { - return requestJson( - "/io/fs/list/absolute", - { method: "POST", body: JSON.stringify({ path }) }, - baseUrl, - ); - }, - createDirectoryAbsolute(path: string, baseUrl?: string) { - return requestJson( - "/io/fs/create/absolute", - { method: "POST", body: JSON.stringify({ path }) }, - baseUrl, - ); - }, - getHomeDirectory(baseUrl?: string) { - return requestJson("/io/fs/home", {}, baseUrl); - }, - listProjectFiles(baseUrl?: string) { - return requestJson("/io/fs/files", {}, baseUrl); - }, - searchFiles(query: string, baseUrl?: string) { - return requestJson( - "/fs/search", - { method: "POST", body: JSON.stringify({ query }) }, - baseUrl, - ); - }, - execShell(command: string, args: string[], baseUrl?: string) { - return requestJson( - "/shell/exec", - { method: "POST", body: JSON.stringify({ command, args }) }, - baseUrl, - ); - }, - cancelChat(baseUrl?: string) { - return requestJson("/chat/cancel", { method: "POST" }, baseUrl); - }, - getWorkItemContent(storyId: string, baseUrl?: string) { - return requestJson( - `/work-items/${encodeURIComponent(storyId)}`, - {}, - baseUrl, - ); - }, - getTestResults(storyId: string, baseUrl?: string) { - return requestJson( - `/work-items/${encodeURIComponent(storyId)}/test-results`, - {}, - baseUrl, - ); - }, - getTokenCost(storyId: string, baseUrl?: string) { - return requestJson( - `/work-items/${encodeURIComponent(storyId)}/token-cost`, - {}, - baseUrl, - ); - }, - getAllTokenUsage(baseUrl?: string) { - return requestJson("/token-usage", {}, baseUrl); - }, - /** Trigger a server rebuild and restart. */ - rebuildAndRestart() { - return callMcpTool("rebuild_and_restart", {}); - }, - /** Approve a story in QA, moving it to merge. */ - approveQa(storyId: string) { - return callMcpTool("approve_qa", { story_id: storyId }); - }, - /** Reject a story in QA, moving it back to current with notes. */ - rejectQa(storyId: string, notes: string) { - return callMcpTool("reject_qa", { story_id: storyId, notes }); - }, - /** Launch the QA app for a story's worktree. */ - launchQaApp(storyId: string) { - return callMcpTool("launch_qa_app", { story_id: storyId }); - }, - /** Delete a story from the pipeline, stopping any running agent and removing the worktree. */ - deleteStory(storyId: string) { - return callMcpTool("delete_story", { story_id: storyId }); - }, -}; - -async function callMcpTool( - toolName: string, - args: Record, -): Promise { - const res = await fetch("/mcp", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - jsonrpc: "2.0", - id: 1, - method: "tools/call", - params: { name: toolName, arguments: args }, - }), - }); - const json = await res.json(); - if (json.error) { - throw new Error(json.error.message); - } - const text = json.result?.content?.[0]?.text ?? ""; - return text; -} - -export class ChatWebSocket { - private static sharedSocket: WebSocket | null = null; - private static refCount = 0; - private socket?: WebSocket; - private onToken?: (content: string) => void; - private onThinkingToken?: (content: string) => void; - private onUpdate?: (messages: Message[]) => void; - private onSessionId?: (sessionId: string) => void; - private onError?: (message: string) => void; - private onPipelineState?: (state: PipelineState) => void; - private onPermissionRequest?: ( - requestId: string, - toolName: string, - toolInput: Record, - ) => void; - private onActivity?: (toolName: string) => void; - private onReconciliationProgress?: ( - storyId: string, - status: string, - message: string, - ) => void; - private onAgentConfigChanged?: () => void; - private onAgentStateChanged?: () => void; - private onOnboardingStatus?: (needsOnboarding: boolean) => void; - private onSideQuestionToken?: (content: string) => void; - private onSideQuestionDone?: (response: string) => void; - private onLogEntry?: ( - timestamp: string, - level: string, - message: string, - ) => void; - private onConnected?: () => void; - private connected = false; - private closeTimer?: number; - private wsPath = DEFAULT_WS_PATH; - private reconnectTimer?: number; - private reconnectDelay = 1000; - private shouldReconnect = false; - private heartbeatInterval?: number; - private heartbeatTimeout?: number; - private static readonly HEARTBEAT_INTERVAL = 30_000; - private static readonly HEARTBEAT_TIMEOUT = 5_000; - - private _startHeartbeat(): void { - this._stopHeartbeat(); - this.heartbeatInterval = window.setInterval(() => { - if (!this.socket || this.socket.readyState !== WebSocket.OPEN) return; - const ping: WsRequest = { type: "ping" }; - this.socket.send(JSON.stringify(ping)); - this.heartbeatTimeout = window.setTimeout(() => { - // No pong received within timeout; close socket to trigger reconnect. - this.socket?.close(); - }, ChatWebSocket.HEARTBEAT_TIMEOUT); - }, ChatWebSocket.HEARTBEAT_INTERVAL); - } - - private _stopHeartbeat(): void { - window.clearInterval(this.heartbeatInterval); - window.clearTimeout(this.heartbeatTimeout); - this.heartbeatInterval = undefined; - this.heartbeatTimeout = undefined; - } - - private _buildWsUrl(): string { - const protocol = window.location.protocol === "https:" ? "wss" : "ws"; - const wsHost = resolveWsHost( - import.meta.env.DEV, - typeof __STORKIT_PORT__ !== "undefined" ? __STORKIT_PORT__ : undefined, - window.location.host, - ); - return `${protocol}://${wsHost}${this.wsPath}`; - } - - private _attachHandlers(): void { - if (!this.socket) return; - this.socket.onopen = () => { - this.reconnectDelay = 1000; - this._startHeartbeat(); - this.onConnected?.(); - }; - this.socket.onmessage = (event) => { - try { - const data = JSON.parse(event.data) as WsResponse; - if (data.type === "token") this.onToken?.(data.content); - if (data.type === "thinking_token") - this.onThinkingToken?.(data.content); - if (data.type === "update") this.onUpdate?.(data.messages); - if (data.type === "session_id") this.onSessionId?.(data.session_id); - if (data.type === "error") this.onError?.(data.message); - if (data.type === "pipeline_state") - this.onPipelineState?.({ - backlog: data.backlog, - current: data.current, - qa: data.qa, - merge: data.merge, - done: data.done, - }); - if (data.type === "permission_request") - this.onPermissionRequest?.( - data.request_id, - data.tool_name, - data.tool_input, - ); - if (data.type === "tool_activity") this.onActivity?.(data.tool_name); - if (data.type === "reconciliation_progress") - this.onReconciliationProgress?.( - data.story_id, - data.status, - data.message, - ); - if (data.type === "agent_config_changed") this.onAgentConfigChanged?.(); - if (data.type === "agent_state_changed") this.onAgentStateChanged?.(); - if (data.type === "onboarding_status") - this.onOnboardingStatus?.(data.needs_onboarding); - if (data.type === "side_question_token") - this.onSideQuestionToken?.(data.content); - if (data.type === "side_question_done") - this.onSideQuestionDone?.(data.response); - if (data.type === "log_entry") - this.onLogEntry?.(data.timestamp, data.level, data.message); - if (data.type === "pong") { - window.clearTimeout(this.heartbeatTimeout); - this.heartbeatTimeout = undefined; - } - } catch (err) { - this.onError?.(String(err)); - } - }; - this.socket.onerror = () => { - this.onError?.("WebSocket error"); - }; - this.socket.onclose = () => { - if (this.shouldReconnect && this.connected) { - this._scheduleReconnect(); - } - }; - } - - private _scheduleReconnect(): void { - window.clearTimeout(this.reconnectTimer); - const delay = this.reconnectDelay; - this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000); - this.reconnectTimer = window.setTimeout(() => { - this.reconnectTimer = undefined; - const wsUrl = this._buildWsUrl(); - ChatWebSocket.sharedSocket = new WebSocket(wsUrl); - this.socket = ChatWebSocket.sharedSocket; - this._attachHandlers(); - }, delay); - } - - connect( - handlers: { - onToken?: (content: string) => void; - onThinkingToken?: (content: string) => void; - onUpdate?: (messages: Message[]) => void; - onSessionId?: (sessionId: string) => void; - onError?: (message: string) => void; - onPipelineState?: (state: PipelineState) => void; - onPermissionRequest?: ( - requestId: string, - toolName: string, - toolInput: Record, - ) => void; - onActivity?: (toolName: string) => void; - onReconciliationProgress?: ( - storyId: string, - status: string, - message: string, - ) => void; - onAgentConfigChanged?: () => void; - onAgentStateChanged?: () => void; - onOnboardingStatus?: (needsOnboarding: boolean) => void; - onSideQuestionToken?: (content: string) => void; - onSideQuestionDone?: (response: string) => void; - onLogEntry?: (timestamp: string, level: string, message: string) => void; - onConnected?: () => void; - }, - wsPath = DEFAULT_WS_PATH, - ) { - this.onToken = handlers.onToken; - this.onThinkingToken = handlers.onThinkingToken; - this.onUpdate = handlers.onUpdate; - this.onSessionId = handlers.onSessionId; - this.onError = handlers.onError; - this.onPipelineState = handlers.onPipelineState; - this.onPermissionRequest = handlers.onPermissionRequest; - this.onActivity = handlers.onActivity; - this.onReconciliationProgress = handlers.onReconciliationProgress; - this.onAgentConfigChanged = handlers.onAgentConfigChanged; - this.onAgentStateChanged = handlers.onAgentStateChanged; - this.onOnboardingStatus = handlers.onOnboardingStatus; - this.onSideQuestionToken = handlers.onSideQuestionToken; - this.onSideQuestionDone = handlers.onSideQuestionDone; - this.onLogEntry = handlers.onLogEntry; - this.onConnected = handlers.onConnected; - this.wsPath = wsPath; - this.shouldReconnect = true; - - if (this.connected) { - return; - } - this.connected = true; - ChatWebSocket.refCount += 1; - - if ( - !ChatWebSocket.sharedSocket || - ChatWebSocket.sharedSocket.readyState === WebSocket.CLOSED || - ChatWebSocket.sharedSocket.readyState === WebSocket.CLOSING - ) { - const wsUrl = this._buildWsUrl(); - ChatWebSocket.sharedSocket = new WebSocket(wsUrl); - } - this.socket = ChatWebSocket.sharedSocket; - this._attachHandlers(); - } - - sendChat(messages: Message[], config: ProviderConfig) { - this.send({ type: "chat", messages, config }); - } - - sendSideQuestion( - question: string, - contextMessages: Message[], - config: ProviderConfig, - ) { - this.send({ - type: "side_question", - question, - context_messages: contextMessages, - config, - }); - } - - cancel() { - this.send({ type: "cancel" }); - } - - sendPermissionResponse( - requestId: string, - approved: boolean, - alwaysAllow = false, - ) { - this.send({ - type: "permission_response", - request_id: requestId, - approved, - always_allow: alwaysAllow, - }); - } - - close() { - this.shouldReconnect = false; - this._stopHeartbeat(); - window.clearTimeout(this.reconnectTimer); - this.reconnectTimer = undefined; - - if (!this.connected) return; - this.connected = false; - ChatWebSocket.refCount = Math.max(0, ChatWebSocket.refCount - 1); - - if (import.meta.env.DEV) { - if (this.closeTimer) { - window.clearTimeout(this.closeTimer); - } - this.closeTimer = window.setTimeout(() => { - if (ChatWebSocket.refCount === 0) { - ChatWebSocket.sharedSocket?.close(); - ChatWebSocket.sharedSocket = null; - } - this.socket = ChatWebSocket.sharedSocket ?? undefined; - this.closeTimer = undefined; - }, 250); - return; - } - - if (ChatWebSocket.refCount === 0) { - ChatWebSocket.sharedSocket?.close(); - ChatWebSocket.sharedSocket = null; - } - this.socket = ChatWebSocket.sharedSocket ?? undefined; - } - - private send(payload: WsRequest) { - if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { - this.onError?.("WebSocket is not connected"); - return; - } - this.socket.send(JSON.stringify(payload)); - } -} diff --git a/frontend/src/api/settings.test.ts b/frontend/src/api/settings.test.ts deleted file mode 100644 index 963799e..0000000 --- a/frontend/src/api/settings.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { settingsApi } from "./settings"; - -const mockFetch = vi.fn(); - -beforeEach(() => { - vi.stubGlobal("fetch", mockFetch); -}); - -afterEach(() => { - vi.restoreAllMocks(); -}); - -function okResponse(body: unknown) { - return new Response(JSON.stringify(body), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); -} - -function errorResponse(status: number, text: string) { - return new Response(text, { status }); -} - -describe("settingsApi", () => { - describe("getEditorCommand", () => { - it("sends GET to /settings/editor and returns editor settings", async () => { - const expected = { editor_command: "zed" }; - mockFetch.mockResolvedValueOnce(okResponse(expected)); - - const result = await settingsApi.getEditorCommand(); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/settings/editor", - expect.objectContaining({ - headers: expect.objectContaining({ - "Content-Type": "application/json", - }), - }), - ); - expect(result).toEqual(expected); - }); - - it("returns null editor_command when not configured", async () => { - const expected = { editor_command: null }; - mockFetch.mockResolvedValueOnce(okResponse(expected)); - - const result = await settingsApi.getEditorCommand(); - expect(result.editor_command).toBeNull(); - }); - - it("uses custom baseUrl when provided", async () => { - mockFetch.mockResolvedValueOnce(okResponse({ editor_command: "code" })); - - await settingsApi.getEditorCommand("http://localhost:4000/api"); - - expect(mockFetch).toHaveBeenCalledWith( - "http://localhost:4000/api/settings/editor", - expect.anything(), - ); - }); - }); - - describe("setEditorCommand", () => { - it("sends PUT to /settings/editor with command body", async () => { - const expected = { editor_command: "zed" }; - mockFetch.mockResolvedValueOnce(okResponse(expected)); - - const result = await settingsApi.setEditorCommand("zed"); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/settings/editor", - expect.objectContaining({ - method: "PUT", - body: JSON.stringify({ editor_command: "zed" }), - }), - ); - expect(result).toEqual(expected); - }); - - it("sends PUT with null to clear the editor command", async () => { - const expected = { editor_command: null }; - mockFetch.mockResolvedValueOnce(okResponse(expected)); - - const result = await settingsApi.setEditorCommand(null); - - expect(mockFetch).toHaveBeenCalledWith( - "/api/settings/editor", - expect.objectContaining({ - method: "PUT", - body: JSON.stringify({ editor_command: null }), - }), - ); - expect(result.editor_command).toBeNull(); - }); - - it("uses custom baseUrl when provided", async () => { - mockFetch.mockResolvedValueOnce(okResponse({ editor_command: "vim" })); - - await settingsApi.setEditorCommand("vim", "http://localhost:4000/api"); - - expect(mockFetch).toHaveBeenCalledWith( - "http://localhost:4000/api/settings/editor", - expect.objectContaining({ method: "PUT" }), - ); - }); - }); - - describe("error handling", () => { - it("throws with response body text on non-ok response", async () => { - mockFetch.mockResolvedValueOnce(errorResponse(400, "Bad Request")); - - await expect(settingsApi.getEditorCommand()).rejects.toThrow( - "Bad Request", - ); - }); - - it("throws with status code message when response body is empty", async () => { - mockFetch.mockResolvedValueOnce(errorResponse(500, "")); - - await expect(settingsApi.getEditorCommand()).rejects.toThrow( - "Request failed (500)", - ); - }); - - it("throws on setEditorCommand error", async () => { - mockFetch.mockResolvedValueOnce(errorResponse(403, "Forbidden")); - - await expect(settingsApi.setEditorCommand("code")).rejects.toThrow( - "Forbidden", - ); - }); - }); -}); diff --git a/frontend/src/api/settings.ts b/frontend/src/api/settings.ts deleted file mode 100644 index 21c36f2..0000000 --- a/frontend/src/api/settings.ts +++ /dev/null @@ -1,70 +0,0 @@ -export interface EditorSettings { - editor_command: string | null; -} - -export interface OpenFileResult { - success: boolean; -} - -const DEFAULT_API_BASE = "/api"; - -function buildApiUrl(path: string, baseUrl = DEFAULT_API_BASE): string { - return `${baseUrl}${path}`; -} - -async function requestJson( - path: string, - options: RequestInit = {}, - baseUrl = DEFAULT_API_BASE, -): Promise { - const res = await fetch(buildApiUrl(path, baseUrl), { - headers: { - "Content-Type": "application/json", - ...(options.headers ?? {}), - }, - ...options, - }); - - if (!res.ok) { - const text = await res.text(); - throw new Error(text || `Request failed (${res.status})`); - } - - return res.json() as Promise; -} - -export const settingsApi = { - getEditorCommand(baseUrl?: string): Promise { - return requestJson("/settings/editor", {}, baseUrl); - }, - - setEditorCommand( - command: string | null, - baseUrl?: string, - ): Promise { - return requestJson( - "/settings/editor", - { - method: "PUT", - body: JSON.stringify({ editor_command: command }), - }, - baseUrl, - ); - }, - - openFile( - path: string, - line?: number, - baseUrl?: string, - ): Promise { - const params = new URLSearchParams({ path }); - if (line !== undefined) { - params.set("line", String(line)); - } - return requestJson( - `/settings/open-file?${params.toString()}`, - { method: "POST" }, - baseUrl, - ); - }, -}; diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/frontend/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/components/AgentPanel.test.tsx b/frontend/src/components/AgentPanel.test.tsx deleted file mode 100644 index 1931c9f..0000000 --- a/frontend/src/components/AgentPanel.test.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import { act, render, screen } from "@testing-library/react"; -import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type { AgentConfigInfo, AgentEvent, AgentInfo } from "../api/agents"; -import { agentsApi, subscribeAgentStream } from "../api/agents"; - -vi.mock("../api/agents", () => { - const agentsApi = { - listAgents: vi.fn(), - getAgentConfig: vi.fn(), - startAgent: vi.fn(), - stopAgent: vi.fn(), - reloadConfig: vi.fn(), - }; - return { agentsApi, subscribeAgentStream: vi.fn(() => () => {}) }; -}); - -// Dynamic import so the mock is in place before the module loads -const { AgentPanel } = await import("./AgentPanel"); - -const mockedSubscribeAgentStream = vi.mocked(subscribeAgentStream); - -const mockedAgents = { - listAgents: vi.mocked(agentsApi.listAgents), - getAgentConfig: vi.mocked(agentsApi.getAgentConfig), - startAgent: vi.mocked(agentsApi.startAgent), -}; - -const ROSTER: AgentConfigInfo[] = [ - { - name: "coder-1", - role: "Full-stack engineer", - stage: "coder", - model: "sonnet", - allowed_tools: null, - max_turns: 50, - max_budget_usd: 5.0, - }, -]; - -describe("AgentPanel active work list removed", () => { - beforeAll(() => { - Element.prototype.scrollIntoView = vi.fn(); - }); - - beforeEach(() => { - mockedAgents.getAgentConfig.mockResolvedValue(ROSTER); - mockedAgents.listAgents.mockResolvedValue([]); - }); - - it("does not render active agent entries even when agents are running", async () => { - const agentList: AgentInfo[] = [ - { - story_id: "83_active", - agent_name: "coder-1", - status: "running", - session_id: null, - worktree_path: "/tmp/wt", - base_branch: "master", - log_session_id: null, - }, - ]; - mockedAgents.listAgents.mockResolvedValue(agentList); - - const { container } = render(); - - // Roster badge should still be visible - await screen.findByTestId("roster-badge-coder-1"); - - // No agent entry divs should exist - expect( - container.querySelector('[data-testid^="agent-entry-"]'), - ).not.toBeInTheDocument(); - }); -}); - -describe("Running count visibility in header", () => { - beforeAll(() => { - Element.prototype.scrollIntoView = vi.fn(); - }); - - beforeEach(() => { - mockedAgents.getAgentConfig.mockResolvedValue(ROSTER); - mockedAgents.listAgents.mockResolvedValue([]); - }); - - // AC1: When no agents are running, "0 running" is NOT visible - it("does not show running count when no agents are running", async () => { - render(); - - // Wait for roster to load - await screen.findByTestId("roster-badge-coder-1"); - - expect(screen.queryByText(/0 running/)).not.toBeInTheDocument(); - }); - - // AC2: When agents are running, "N running" IS visible - it("shows running count when agents are running", async () => { - const agentList: AgentInfo[] = [ - { - story_id: "99_active", - agent_name: "coder-1", - status: "running", - session_id: null, - worktree_path: "/tmp/wt", - base_branch: "master", - log_session_id: null, - }, - ]; - mockedAgents.listAgents.mockResolvedValue(agentList); - - render(); - - await screen.findByText(/1 running/); - }); -}); - -describe("RosterBadge availability state", () => { - beforeAll(() => { - Element.prototype.scrollIntoView = vi.fn(); - }); - - beforeEach(() => { - mockedAgents.getAgentConfig.mockResolvedValue(ROSTER); - mockedAgents.listAgents.mockResolvedValue([]); - }); - - it("shows a green dot for an idle agent", async () => { - render(); - - const dot = await screen.findByTestId("roster-dot-coder-1"); - // JSDOM normalizes #3fb950 to rgb(63, 185, 80) - expect(dot.style.background).toBe("rgb(63, 185, 80)"); - expect(dot.style.animation).toBe(""); - }); - - it("shows grey badge styling for an idle agent", async () => { - render(); - - const badge = await screen.findByTestId("roster-badge-coder-1"); - // JSDOM normalizes #aaa18 to rgba(170, 170, 170, 0.094) and #aaa to rgb(170, 170, 170) - expect(badge.style.background).toBe("rgba(170, 170, 170, 0.094)"); - expect(badge.style.color).toBe("rgb(170, 170, 170)"); - }); - - // AC1: roster badge always shows idle (grey) even when agent is running - it("shows a static green dot for a running agent (roster always idle)", async () => { - const agentList: AgentInfo[] = [ - { - story_id: "81_active", - agent_name: "coder-1", - status: "running", - session_id: null, - worktree_path: null, - base_branch: null, - log_session_id: null, - }, - ]; - mockedAgents.listAgents.mockResolvedValue(agentList); - - render(); - - const dot = await screen.findByTestId("roster-dot-coder-1"); - expect(dot.style.background).toBe("rgb(63, 185, 80)"); - // Roster is always idle — no pulsing animation - expect(dot.style.animation).toBe(""); - }); - - // AC1: roster badge always shows idle (grey) even when agent is running - it("shows grey (idle) badge styling for a running agent", async () => { - const agentList: AgentInfo[] = [ - { - story_id: "81_active", - agent_name: "coder-1", - status: "running", - session_id: null, - worktree_path: null, - base_branch: null, - log_session_id: null, - }, - ]; - mockedAgents.listAgents.mockResolvedValue(agentList); - - render(); - - const badge = await screen.findByTestId("roster-badge-coder-1"); - // Always idle: grey background and grey text - expect(badge.style.background).toBe("rgba(170, 170, 170, 0.094)"); - expect(badge.style.color).toBe("rgb(170, 170, 170)"); - }); - - // AC2: after agent completes and returns to roster, badge shows idle - it("shows idle state after agent status changes from running to completed", async () => { - const agentList: AgentInfo[] = [ - { - story_id: "81_completed", - agent_name: "coder-1", - status: "completed", - session_id: null, - worktree_path: null, - base_branch: null, - log_session_id: null, - }, - ]; - mockedAgents.listAgents.mockResolvedValue(agentList); - - render(); - - const badge = await screen.findByTestId("roster-badge-coder-1"); - const dot = screen.getByTestId("roster-dot-coder-1"); - // Completed agent: badge is idle - expect(badge.style.background).toBe("rgba(170, 170, 170, 0.094)"); - expect(badge.style.color).toBe("rgb(170, 170, 170)"); - expect(dot.style.animation).toBe(""); - }); -}); - -describe("Agent output not shown in sidebar (story 290)", () => { - beforeAll(() => { - Element.prototype.scrollIntoView = vi.fn(); - }); - - beforeEach(() => { - mockedAgents.getAgentConfig.mockResolvedValue(ROSTER); - mockedAgents.listAgents.mockResolvedValue([]); - mockedSubscribeAgentStream.mockReturnValue(() => {}); - }); - - // AC1: output events do not appear in the agents sidebar - it("does not render agent output when output event arrives", async () => { - let emitEvent: ((e: AgentEvent) => void) | null = null; - mockedSubscribeAgentStream.mockImplementation( - (_storyId, _agentName, onEvent) => { - emitEvent = onEvent; - return () => {}; - }, - ); - - const agentList: AgentInfo[] = [ - { - story_id: "290_output", - agent_name: "coder-1", - status: "running", - session_id: null, - worktree_path: "/tmp/wt", - base_branch: "master", - log_session_id: null, - }, - ]; - mockedAgents.listAgents.mockResolvedValue(agentList); - - const { container } = render(); - await screen.findByTestId("roster-badge-coder-1"); - - await act(async () => { - emitEvent?.({ - type: "output", - story_id: "290_output", - agent_name: "coder-1", - text: "doing some work...", - }); - }); - - // No output elements in the sidebar - expect( - container.querySelector('[data-testid^="agent-output-"]'), - ).not.toBeInTheDocument(); - expect( - container.querySelector('[data-testid^="agent-stream-"]'), - ).not.toBeInTheDocument(); - }); - - // AC1: thinking events do not appear in the agents sidebar - it("does not render thinking block when thinking event arrives", async () => { - let emitEvent: ((e: AgentEvent) => void) | null = null; - mockedSubscribeAgentStream.mockImplementation( - (_storyId, _agentName, onEvent) => { - emitEvent = onEvent; - return () => {}; - }, - ); - - const agentList: AgentInfo[] = [ - { - story_id: "290_thinking", - agent_name: "coder-1", - status: "running", - session_id: null, - worktree_path: "/tmp/wt", - base_branch: "master", - log_session_id: null, - }, - ]; - mockedAgents.listAgents.mockResolvedValue(agentList); - - render(); - await screen.findByTestId("roster-badge-coder-1"); - - await act(async () => { - emitEvent?.({ - type: "thinking", - story_id: "290_thinking", - agent_name: "coder-1", - text: "Let me consider the problem carefully...", - }); - }); - - // No thinking block or output in sidebar - expect(screen.queryByTestId("thinking-block")).not.toBeInTheDocument(); - expect( - screen.queryByText("Let me consider the problem carefully..."), - ).not.toBeInTheDocument(); - }); -}); diff --git a/frontend/src/components/AgentPanel.tsx b/frontend/src/components/AgentPanel.tsx deleted file mode 100644 index 404f61f..0000000 --- a/frontend/src/components/AgentPanel.tsx +++ /dev/null @@ -1,419 +0,0 @@ -import * as React from "react"; -import type { - AgentConfigInfo, - AgentEvent, - AgentStatusValue, -} from "../api/agents"; -import { agentsApi, subscribeAgentStream } from "../api/agents"; -import { settingsApi } from "../api/settings"; -import { useLozengeFly } from "./LozengeFlyContext"; - -const { useCallback, useEffect, useRef, useState } = React; - -interface AgentState { - agentName: string; - status: AgentStatusValue; - sessionId: string | null; - worktreePath: string | null; - baseBranch: string | null; - terminalAt: number | null; -} - -const formatTimestamp = (value: Date | null): string => { - if (!value) return ""; - return value.toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - }); -}; - -function RosterBadge({ agent }: { agent: AgentConfigInfo }) { - const { registerRosterEl } = useLozengeFly(); - const badgeRef = useRef(null); - - // Register this element so fly animations know where to start/end - useEffect(() => { - const el = badgeRef.current; - if (el) registerRosterEl(agent.name, el); - return () => registerRosterEl(agent.name, null); - }, [agent.name, registerRosterEl]); - - return ( - - - {agent.name} - {agent.model && {agent.model}} - available - - ); -} - -/** Build a composite key for tracking agent state. */ -function agentKey(storyId: string, agentName: string): string { - return `${storyId}:${agentName}`; -} - -interface AgentPanelProps { - /** Increment this to trigger a re-fetch of the agent roster. */ - configVersion?: number; - /** Increment this to trigger a re-fetch of the agent list (agent state changed). */ - stateVersion?: number; -} - -export function AgentPanel({ - configVersion = 0, - stateVersion = 0, -}: AgentPanelProps) { - const { hiddenRosterAgents } = useLozengeFly(); - const [agents, setAgents] = useState>({}); - const [roster, setRoster] = useState([]); - const [actionError, setActionError] = useState(null); - const [lastRefresh, setLastRefresh] = useState(null); - const [editorCommand, setEditorCommand] = useState(null); - const [editorInput, setEditorInput] = useState(""); - const [editingEditor, setEditingEditor] = useState(false); - const cleanupRefs = useRef void>>({}); - - // Re-fetch roster whenever configVersion changes (triggered by agent_config_changed WS event). - useEffect(() => { - agentsApi - .getAgentConfig() - .then(setRoster) - .catch((err) => console.error("Failed to load agent config:", err)); - }, [configVersion]); - - const subscribeToAgent = useCallback((storyId: string, agentName: string) => { - const key = agentKey(storyId, agentName); - cleanupRefs.current[key]?.(); - - const cleanup = subscribeAgentStream( - storyId, - agentName, - (event: AgentEvent) => { - setAgents((prev) => { - const current = prev[key] ?? { - agentName, - status: "pending" as AgentStatusValue, - sessionId: null, - worktreePath: null, - baseBranch: null, - terminalAt: null, - }; - - switch (event.type) { - case "status": { - const newStatus = - (event.status as AgentStatusValue) ?? current.status; - const isTerminal = - newStatus === "completed" || newStatus === "failed"; - return { - ...prev, - [key]: { - ...current, - status: newStatus, - terminalAt: isTerminal - ? (current.terminalAt ?? Date.now()) - : current.terminalAt, - }, - }; - } - case "done": - return { - ...prev, - [key]: { - ...current, - status: "completed", - sessionId: event.session_id ?? current.sessionId, - terminalAt: current.terminalAt ?? Date.now(), - }, - }; - case "error": - return { - ...prev, - [key]: { - ...current, - status: "failed", - terminalAt: current.terminalAt ?? Date.now(), - }, - }; - default: - // output, thinking, and other events are not displayed in the sidebar. - // Agent output streams appear in the work item detail panel instead. - return prev; - } - }); - }, - () => { - // SSE error — agent may not be streaming yet - }, - ); - - cleanupRefs.current[key] = cleanup; - }, []); - - /** Shared helper: fetch the agent list and update state + SSE subscriptions. */ - const refreshAgents = useCallback(() => { - agentsApi - .listAgents() - .then((agentList) => { - const agentMap: Record = {}; - const now = Date.now(); - for (const a of agentList) { - const key = agentKey(a.story_id, a.agent_name); - const isTerminal = a.status === "completed" || a.status === "failed"; - agentMap[key] = { - agentName: a.agent_name, - status: a.status, - sessionId: a.session_id, - worktreePath: a.worktree_path, - baseBranch: a.base_branch, - terminalAt: isTerminal ? now : null, - }; - if (a.status === "running" || a.status === "pending") { - subscribeToAgent(a.story_id, a.agent_name); - } - } - setAgents(agentMap); - setLastRefresh(new Date()); - }) - .catch((err) => console.error("Failed to load agents:", err)); - }, [subscribeToAgent]); - - // Load existing agents and editor preference on mount - useEffect(() => { - refreshAgents(); - - settingsApi - .getEditorCommand() - .then((s) => { - setEditorCommand(s.editor_command); - setEditorInput(s.editor_command ?? ""); - }) - .catch((err) => console.error("Failed to load editor command:", err)); - - return () => { - for (const cleanup of Object.values(cleanupRefs.current)) { - cleanup(); - } - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - // Re-fetch agent list when agent state changes (via WebSocket notification). - // Skip the initial render (stateVersion=0) since the mount effect handles that. - useEffect(() => { - if (stateVersion > 0) { - refreshAgents(); - } - }, [stateVersion, refreshAgents]); - - const handleSaveEditor = async () => { - try { - const trimmed = editorInput.trim() || null; - const result = await settingsApi.setEditorCommand(trimmed); - setEditorCommand(result.editor_command); - setEditorInput(result.editor_command ?? ""); - setEditingEditor(false); - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - setActionError(`Failed to save editor: ${message}`); - } - }; - - return ( -
-
-
-
Agents
- {Object.values(agents).filter((a) => a.status === "running").length > - 0 && ( -
- { - Object.values(agents).filter((a) => a.status === "running") - .length - }{" "} - running -
- )} -
- {lastRefresh && ( -
- Loaded {formatTimestamp(lastRefresh)} -
- )} -
- - {/* Editor preference */} -
- Editor: - {editingEditor ? ( - <> - setEditorInput(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") handleSaveEditor(); - if (e.key === "Escape") setEditingEditor(false); - }} - placeholder="zed, code, cursor..." - style={{ - fontSize: "0.75em", - background: "#111", - border: "1px solid #444", - borderRadius: "4px", - color: "#ccc", - padding: "2px 6px", - width: "120px", - }} - /> - - - - ) : ( - - )} -
- - {/* Roster badges — agents always display in idle state here */} - {roster.length > 0 && ( -
- {roster.map((a) => { - const isHidden = hiddenRosterAgents.has(a.name); - return ( -
- -
- ); - })} -
- )} - - {actionError && ( -
- {actionError} -
- )} -
- ); -} diff --git a/frontend/src/components/Chat.test.tsx b/frontend/src/components/Chat.test.tsx deleted file mode 100644 index 929f144..0000000 --- a/frontend/src/components/Chat.test.tsx +++ /dev/null @@ -1,1459 +0,0 @@ -import { - act, - fireEvent, - render, - screen, - waitFor, -} from "@testing-library/react"; - -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { api } from "../api/client"; -import type { Message } from "../types"; -import { Chat } from "./Chat"; - -// Module-level store for the WebSocket handlers captured during connect(). -// Tests in the "message rendering" suite use this to simulate incoming messages. -type WsHandlers = { - onToken: (content: string) => void; - onUpdate: (history: Message[]) => void; - onSessionId: (sessionId: string) => void; - onError: (message: string) => void; - onActivity: (toolName: string) => void; - onReconciliationProgress: ( - storyId: string, - status: string, - message: string, - ) => void; -}; -let capturedWsHandlers: WsHandlers | null = null; -// Captures the last sendChat call's arguments for assertion. -let lastSendChatArgs: { messages: Message[]; config: unknown } | null = null; - -vi.mock("../api/client", () => { - const api = { - getOllamaModels: vi.fn(), - getAnthropicApiKeyExists: vi.fn(), - getAnthropicModels: vi.fn(), - getModelPreference: vi.fn(), - setModelPreference: vi.fn(), - cancelChat: vi.fn(), - setAnthropicApiKey: vi.fn(), - readFile: vi.fn(), - listProjectFiles: vi.fn(), - }; - class ChatWebSocket { - connect(handlers: WsHandlers) { - capturedWsHandlers = handlers; - } - close() {} - sendChat(messages: Message[], config: unknown) { - lastSendChatArgs = { messages, config }; - } - cancel() {} - } - return { api, ChatWebSocket }; -}); - -const mockedApi = { - getOllamaModels: vi.mocked(api.getOllamaModels), - getAnthropicApiKeyExists: vi.mocked(api.getAnthropicApiKeyExists), - getAnthropicModels: vi.mocked(api.getAnthropicModels), - getModelPreference: vi.mocked(api.getModelPreference), - setModelPreference: vi.mocked(api.setModelPreference), - cancelChat: vi.mocked(api.cancelChat), - setAnthropicApiKey: vi.mocked(api.setAnthropicApiKey), - readFile: vi.mocked(api.readFile), - listProjectFiles: vi.mocked(api.listProjectFiles), -}; - -function setupMocks() { - mockedApi.getOllamaModels.mockResolvedValue(["llama3.1"]); - mockedApi.getAnthropicApiKeyExists.mockResolvedValue(true); - mockedApi.getAnthropicModels.mockResolvedValue([]); - mockedApi.getModelPreference.mockResolvedValue(null); - mockedApi.setModelPreference.mockResolvedValue(true); - mockedApi.readFile.mockResolvedValue(""); - mockedApi.listProjectFiles.mockResolvedValue([]); - mockedApi.cancelChat.mockResolvedValue(true); - mockedApi.setAnthropicApiKey.mockResolvedValue(true); -} - -describe("Default provider selection (Story 206)", () => { - beforeEach(() => { - capturedWsHandlers = null; - }); - - it("AC1: defaults to claude-code-pty when no saved model preference exists", async () => { - mockedApi.getOllamaModels.mockResolvedValue([]); - mockedApi.getAnthropicApiKeyExists.mockResolvedValue(false); - mockedApi.getAnthropicModels.mockResolvedValue([]); - mockedApi.getModelPreference.mockResolvedValue(null); - mockedApi.setModelPreference.mockResolvedValue(true); - mockedApi.cancelChat.mockResolvedValue(true); - - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // With no models available, the header renders a text input with the model value - const input = screen.getByPlaceholderText("Model"); - expect(input).toHaveValue("claude-code-pty"); - }); - - it("AC2: claude-code-pty remains default even when ollama models are available", async () => { - mockedApi.getOllamaModels.mockResolvedValue(["llama3.1", "deepseek-coder"]); - mockedApi.getAnthropicApiKeyExists.mockResolvedValue(false); - mockedApi.getAnthropicModels.mockResolvedValue([]); - mockedApi.getModelPreference.mockResolvedValue(null); - mockedApi.setModelPreference.mockResolvedValue(true); - mockedApi.cancelChat.mockResolvedValue(true); - - render(); - - // Wait for Ollama models to load and the select dropdown to appear - const select = await screen.findByRole("combobox"); - expect(select).toHaveValue("claude-code-pty"); - }); - - it("AC3: respects saved model preference for existing projects", async () => { - mockedApi.getOllamaModels.mockResolvedValue(["llama3.1"]); - mockedApi.getAnthropicApiKeyExists.mockResolvedValue(false); - mockedApi.getAnthropicModels.mockResolvedValue([]); - mockedApi.getModelPreference.mockResolvedValue("llama3.1"); - mockedApi.setModelPreference.mockResolvedValue(true); - mockedApi.cancelChat.mockResolvedValue(true); - - render(); - - // Wait for models to load and preference to be applied - const select = await screen.findByRole("combobox"); - await waitFor(() => { - expect(select).toHaveValue("llama3.1"); - }); - }); -}); - -describe("Chat message rendering — unified tool call UI", () => { - beforeEach(() => { - capturedWsHandlers = null; - setupMocks(); - }); - - it("renders tool call badge for assistant message with tool_calls (AC3)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const messages: Message[] = [ - { role: "user", content: "Read src/main.rs" }, - { - role: "assistant", - content: "I'll read that file.", - tool_calls: [ - { - id: "toolu_abc", - type: "function", - function: { - name: "Read", - arguments: '{"file_path":"src/main.rs"}', - }, - }, - ], - }, - ]; - - act(() => { - capturedWsHandlers?.onUpdate(messages); - }); - - expect(await screen.findByText("I'll read that file.")).toBeInTheDocument(); - expect(await screen.findByText("Read(src/main.rs)")).toBeInTheDocument(); - }); - - it("renders collapsible tool output for tool role messages (AC3)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const messages: Message[] = [ - { role: "user", content: "Check the file" }, - { - role: "assistant", - content: "", - tool_calls: [ - { - id: "toolu_1", - type: "function", - function: { name: "Read", arguments: '{"file_path":"foo.rs"}' }, - }, - ], - }, - { - role: "tool", - content: 'fn main() { println!("hello"); }', - tool_call_id: "toolu_1", - }, - { role: "assistant", content: "The file contains a main function." }, - ]; - - act(() => { - capturedWsHandlers?.onUpdate(messages); - }); - - expect(await screen.findByText(/Tool Output/)).toBeInTheDocument(); - expect( - await screen.findByText("The file contains a main function."), - ).toBeInTheDocument(); - }); - - it("renders plain assistant message without tool call badges (AC5)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const messages: Message[] = [ - { role: "user", content: "Hello" }, - { role: "assistant", content: "Hi there! How can I help?" }, - ]; - - act(() => { - capturedWsHandlers?.onUpdate(messages); - }); - - expect( - await screen.findByText("Hi there! How can I help?"), - ).toBeInTheDocument(); - expect(screen.queryByText(/Tool Output/)).toBeNull(); - }); - - it("renders multiple tool calls in a single assistant turn (AC3)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const messages: Message[] = [ - { role: "user", content: "Do some work" }, - { - role: "assistant", - content: "I'll do multiple things.", - tool_calls: [ - { - id: "id1", - type: "function", - function: { name: "Bash", arguments: '{"command":"cargo test"}' }, - }, - { - id: "id2", - type: "function", - function: { name: "Read", arguments: '{"file_path":"Cargo.toml"}' }, - }, - ], - }, - ]; - - act(() => { - capturedWsHandlers?.onUpdate(messages); - }); - - expect(await screen.findByText("Bash(cargo test)")).toBeInTheDocument(); - expect(await screen.findByText("Read(Cargo.toml)")).toBeInTheDocument(); - }); - - it("does not fetch Anthropic models when no API key exists", async () => { - mockedApi.getAnthropicApiKeyExists.mockResolvedValue(false); - mockedApi.getAnthropicModels.mockClear(); - - render(); - - await waitFor(() => { - expect(mockedApi.getAnthropicApiKeyExists).toHaveBeenCalled(); - }); - - expect(mockedApi.getAnthropicModels).not.toHaveBeenCalled(); - }); -}); - -describe("Chat two-column layout", () => { - beforeEach(() => { - capturedWsHandlers = null; - setupMocks(); - }); - - it("renders left and right column containers (AC1, AC2)", async () => { - render(); - - expect(await screen.findByTestId("chat-content-area")).toBeInTheDocument(); - expect(await screen.findByTestId("chat-left-column")).toBeInTheDocument(); - expect(await screen.findByTestId("chat-right-column")).toBeInTheDocument(); - }); - - it("renders chat input inside the left column (AC2, AC5)", async () => { - render(); - - const leftColumn = await screen.findByTestId("chat-left-column"); - const input = screen.getByPlaceholderText("Send a message..."); - expect(leftColumn).toContainElement(input); - }); - - it("renders panels inside the right column (AC2)", async () => { - render(); - - const rightColumn = await screen.findByTestId("chat-right-column"); - const agentsPanel = await screen.findByText("Agents"); - expect(rightColumn).toContainElement(agentsPanel); - }); - - it("uses row flex-direction on wide screens (AC3)", async () => { - Object.defineProperty(window, "innerWidth", { - writable: true, - configurable: true, - value: 1200, - }); - window.dispatchEvent(new Event("resize")); - - render(); - - const contentArea = await screen.findByTestId("chat-content-area"); - expect(contentArea).toHaveStyle({ flexDirection: "row" }); - }); - - it("uses column flex-direction on narrow screens (AC4)", async () => { - Object.defineProperty(window, "innerWidth", { - writable: true, - configurable: true, - value: 600, - }); - window.dispatchEvent(new Event("resize")); - - render(); - - const contentArea = await screen.findByTestId("chat-content-area"); - expect(contentArea).toHaveStyle({ flexDirection: "column" }); - - // Restore wide width for subsequent tests - Object.defineProperty(window, "innerWidth", { - writable: true, - configurable: true, - value: 1024, - }); - }); -}); - -describe("Chat input Shift+Enter behavior", () => { - beforeEach(() => { - capturedWsHandlers = null; - setupMocks(); - }); - - it("renders a textarea element for the chat input (AC3)", async () => { - render(); - const input = screen.getByPlaceholderText("Send a message..."); - expect(input.tagName.toLowerCase()).toBe("textarea"); - }); - - it("sends message on Enter key press without Shift (AC2)", async () => { - render(); - const input = screen.getByPlaceholderText("Send a message..."); - - await act(async () => { - fireEvent.change(input, { target: { value: "Hello" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - await waitFor(() => { - expect((input as HTMLTextAreaElement).value).toBe(""); - }); - }); - - it("does not send message on Shift+Enter (AC1)", async () => { - render(); - const input = screen.getByPlaceholderText("Send a message..."); - - await act(async () => { - fireEvent.change(input, { target: { value: "Hello" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: true }); - }); - - expect((input as HTMLTextAreaElement).value).toBe("Hello"); - }); -}); - -describe("Chat reconciliation banner", () => { - beforeEach(() => { - capturedWsHandlers = null; - setupMocks(); - }); - - it("shows banner when a non-done reconciliation event is received", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - act(() => { - capturedWsHandlers?.onReconciliationProgress( - "42_story_test", - "checking", - "Checking for committed work in 2_current/", - ); - }); - - expect( - await screen.findByTestId("reconciliation-banner"), - ).toBeInTheDocument(); - expect( - await screen.findByText("Reconciling startup state..."), - ).toBeInTheDocument(); - }); - - it("shows event message in the banner", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - act(() => { - capturedWsHandlers?.onReconciliationProgress( - "42_story_test", - "gates_running", - "Running acceptance gates…", - ); - }); - - expect( - await screen.findByText(/Running acceptance gates/), - ).toBeInTheDocument(); - }); - - it("dismisses banner when done event is received", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - act(() => { - capturedWsHandlers?.onReconciliationProgress( - "42_story_test", - "checking", - "Checking for committed work", - ); - }); - - expect( - await screen.findByTestId("reconciliation-banner"), - ).toBeInTheDocument(); - - act(() => { - capturedWsHandlers?.onReconciliationProgress( - "", - "done", - "Startup reconciliation complete.", - ); - }); - - await waitFor(() => { - expect( - screen.queryByTestId("reconciliation-banner"), - ).not.toBeInTheDocument(); - }); - }); -}); - -describe("Chat localStorage persistence (Story 145)", () => { - const PROJECT_PATH = "/tmp/project"; - const STORAGE_KEY = `storykit-chat-history:${PROJECT_PATH}`; - - beforeEach(() => { - capturedWsHandlers = null; - localStorage.clear(); - setupMocks(); - }); - - afterEach(() => { - localStorage.clear(); - }); - - it("AC1: restores persisted messages on mount", async () => { - const saved: Message[] = [ - { role: "user", content: "Previously saved question" }, - { role: "assistant", content: "Previously saved answer" }, - ]; - localStorage.setItem(STORAGE_KEY, JSON.stringify(saved)); - - render(); - - expect( - await screen.findByText("Previously saved question"), - ).toBeInTheDocument(); - expect( - await screen.findByText("Previously saved answer"), - ).toBeInTheDocument(); - }); - - it("AC2: persists messages when WebSocket onUpdate fires", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const history: Message[] = [ - { role: "user", content: "Hello" }, - { role: "assistant", content: "Hi there!" }, - ]; - - act(() => { - capturedWsHandlers?.onUpdate(history); - }); - - const stored = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "[]"); - expect(stored).toEqual(history); - }); - - it("AC3: clears localStorage when New Session is clicked", async () => { - const saved: Message[] = [ - { role: "user", content: "Old message" }, - { role: "assistant", content: "Old reply" }, - ]; - localStorage.setItem(STORAGE_KEY, JSON.stringify(saved)); - - // Stub window.confirm to auto-approve the clear dialog - const confirmSpy = vi.spyOn(window, "confirm").mockReturnValue(true); - - render(); - - // Wait for the persisted message to appear - expect(await screen.findByText("Old message")).toBeInTheDocument(); - - // Click "New Session" button - const newSessionBtn = screen.getByText(/New Session/); - await act(async () => { - fireEvent.click(newSessionBtn); - }); - - // localStorage should be cleared - expect(localStorage.getItem(STORAGE_KEY)).toBeNull(); - - // Messages should be gone from the UI - expect(screen.queryByText("Old message")).not.toBeInTheDocument(); - - confirmSpy.mockRestore(); - }); - - it("Bug 245: messages survive unmount/remount cycle (page refresh)", async () => { - // Step 1: Render Chat and populate messages via WebSocket onUpdate - const { unmount } = render( - , - ); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const history: Message[] = [ - { role: "user", content: "Persist me across refresh" }, - { role: "assistant", content: "I should survive a reload" }, - ]; - - act(() => { - capturedWsHandlers?.onUpdate(history); - }); - - // Verify messages are persisted to localStorage - expect(localStorage.getItem(STORAGE_KEY)).not.toBeNull(); - const storedBefore = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "[]"); - expect(storedBefore).toEqual(history); - - // Step 2: Unmount the Chat component (simulates page unload) - unmount(); - - // Verify localStorage was NOT cleared by unmount - expect(localStorage.getItem(STORAGE_KEY)).not.toBeNull(); - const storedAfterUnmount = JSON.parse( - localStorage.getItem(STORAGE_KEY) ?? "[]", - ); - expect(storedAfterUnmount).toEqual(history); - - // Step 3: Remount the Chat component (simulates page reload) - capturedWsHandlers = null; - render(); - - // Verify messages are restored from localStorage - expect( - await screen.findByText("Persist me across refresh"), - ).toBeInTheDocument(); - expect( - await screen.findByText("I should survive a reload"), - ).toBeInTheDocument(); - - // Verify localStorage still has the messages - const storedAfterRemount = JSON.parse( - localStorage.getItem(STORAGE_KEY) ?? "[]", - ); - expect(storedAfterRemount).toEqual(history); - }); - - it("Bug 245: after refresh, sendChat includes full prior history", async () => { - // Step 1: Render, populate messages via onUpdate, then unmount (simulate refresh) - const { unmount } = render( - , - ); - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const priorHistory: Message[] = [ - { role: "user", content: "What is Rust?" }, - { role: "assistant", content: "Rust is a systems programming language." }, - ]; - act(() => { - capturedWsHandlers?.onUpdate(priorHistory); - }); - - // Verify localStorage has the prior history - const stored = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "[]"); - expect(stored).toEqual(priorHistory); - - unmount(); - - // Step 2: Remount (simulates page reload) — messages load from localStorage - capturedWsHandlers = null; - lastSendChatArgs = null; - render(); - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Verify prior messages are displayed - expect(await screen.findByText("What is Rust?")).toBeInTheDocument(); - - // Step 3: Send a new message — sendChat should include the full prior history - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Tell me more" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Verify sendChat was called with ALL prior messages + the new one - expect(lastSendChatArgs).not.toBeNull(); - const args = lastSendChatArgs as unknown as { - messages: Message[]; - config: unknown; - }; - expect(args.messages).toHaveLength(3); - expect(args.messages[0]).toEqual({ - role: "user", - content: "What is Rust?", - }); - expect(args.messages[1]).toEqual({ - role: "assistant", - content: "Rust is a systems programming language.", - }); - expect(args.messages[2]).toEqual({ - role: "user", - content: "Tell me more", - }); - }); - - it("AC5: uses project-scoped storage key", async () => { - const otherKey = "storykit-chat-history:/other/project"; - localStorage.setItem( - otherKey, - JSON.stringify([{ role: "user", content: "Other project msg" }]), - ); - - render(); - - // Should NOT show the other project's messages - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - expect(screen.queryByText("Other project msg")).not.toBeInTheDocument(); - - // Other project's data should still be in storage - expect(localStorage.getItem(otherKey)).not.toBeNull(); - }); -}); - -describe("Chat activity status indicator (Bug 140)", () => { - beforeEach(() => { - capturedWsHandlers = null; - setupMocks(); - }); - - it("shows activity label when tool activity fires during streaming content", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Simulate sending a message to set loading=true - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Read my file" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Simulate tokens arriving (streamingContent becomes non-empty) - act(() => { - capturedWsHandlers?.onToken("I'll read that file for you."); - }); - - // Now simulate a tool activity event while streamingContent is non-empty - act(() => { - capturedWsHandlers?.onActivity("read_file"); - }); - - // The activity indicator should be visible with the tool activity label - const indicator = await screen.findByTestId("activity-indicator"); - expect(indicator).toBeInTheDocument(); - expect(indicator).toHaveTextContent("Reading file..."); - }); - - it("shows Thinking... fallback when loading with no streaming and no activity", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Simulate sending a message to set loading=true - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Hello" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // No tokens, no activity — should show "Thinking..." - const indicator = await screen.findByTestId("activity-indicator"); - expect(indicator).toBeInTheDocument(); - expect(indicator).toHaveTextContent("Thinking..."); - }); - - it("hides Thinking... when streaming content is present but no tool activity", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Simulate sending a message to set loading=true - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Hello" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Tokens arrive — streamingContent is non-empty, no activity - act(() => { - capturedWsHandlers?.onToken("Here is my response..."); - }); - - // The activity indicator should NOT be visible (just streaming bubble) - expect(screen.queryByTestId("activity-indicator")).not.toBeInTheDocument(); - }); - - it("shows activity label for Claude Code tool names (Read, Bash, etc.)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Simulate sending a message to set loading=true - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Read my file" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Simulate tokens arriving - act(() => { - capturedWsHandlers?.onToken("Let me read that."); - }); - - // Claude Code sends tool name "Read" (not "read_file") - act(() => { - capturedWsHandlers?.onActivity("Read"); - }); - - const indicator = await screen.findByTestId("activity-indicator"); - expect(indicator).toBeInTheDocument(); - expect(indicator).toHaveTextContent("Reading file..."); - }); - - it("shows activity label for Claude Code Bash tool", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Run the tests" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - act(() => { - capturedWsHandlers?.onToken("Running tests now."); - }); - - act(() => { - capturedWsHandlers?.onActivity("Bash"); - }); - - const indicator = await screen.findByTestId("activity-indicator"); - expect(indicator).toBeInTheDocument(); - expect(indicator).toHaveTextContent("Executing command..."); - }); - - it("shows generic label for unknown tool names", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Do something" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - act(() => { - capturedWsHandlers?.onToken("Working on it."); - }); - - act(() => { - capturedWsHandlers?.onActivity("SomeCustomTool"); - }); - - const indicator = await screen.findByTestId("activity-indicator"); - expect(indicator).toBeInTheDocument(); - expect(indicator).toHaveTextContent("Using SomeCustomTool..."); - }); -}); - -describe("Chat message queue (Story 155)", () => { - beforeEach(() => { - capturedWsHandlers = null; - setupMocks(); - }); - - it("shows queued message indicator when submitting while loading (AC1, AC2)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Send first message to put the chat in loading state - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "First message" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Now type and submit a second message while loading is true - await act(async () => { - fireEvent.change(input, { target: { value: "Queued message" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // The queued message indicator should appear - const indicator = await screen.findByTestId("queued-message-indicator"); - expect(indicator).toBeInTheDocument(); - expect(indicator).toHaveTextContent("Queued"); - expect(indicator).toHaveTextContent("Queued message"); - - // Input should be cleared after queuing - expect((input as HTMLTextAreaElement).value).toBe(""); - }); - - it("auto-sends queued message when agent response completes (AC4)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - - // Send first message - await act(async () => { - fireEvent.change(input, { target: { value: "First" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Queue a second message while loading - await act(async () => { - fireEvent.change(input, { target: { value: "Auto-send this" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Verify it's queued - expect( - await screen.findByTestId("queued-message-indicator"), - ).toBeInTheDocument(); - - // Simulate agent response completing (loading → false) - act(() => { - capturedWsHandlers?.onUpdate([ - { role: "user", content: "First" }, - { role: "assistant", content: "Done." }, - ]); - }); - - // The queued indicator should disappear (message was sent) - await waitFor(() => { - expect( - screen.queryByTestId("queued-message-indicator"), - ).not.toBeInTheDocument(); - }); - }); - - it("cancel button discards the queued message (AC3, AC6)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - - // Send first message to start loading - await act(async () => { - fireEvent.change(input, { target: { value: "First" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Queue a second message - await act(async () => { - fireEvent.change(input, { target: { value: "Discard me" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - const indicator = await screen.findByTestId("queued-message-indicator"); - expect(indicator).toBeInTheDocument(); - - // Click the ✕ cancel button - const cancelBtn = screen.getByTitle("Cancel queued message"); - await act(async () => { - fireEvent.click(cancelBtn); - }); - - // Indicator should be gone - expect( - screen.queryByTestId("queued-message-indicator"), - ).not.toBeInTheDocument(); - }); - - it("edit button puts queued message back into input (AC3)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - - // Send first message to start loading - await act(async () => { - fireEvent.change(input, { target: { value: "First" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Queue a second message - await act(async () => { - fireEvent.change(input, { target: { value: "Edit me back" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - await screen.findByTestId("queued-message-indicator"); - - // Click the Edit button - const editBtn = screen.getByTitle("Edit queued message"); - await act(async () => { - fireEvent.click(editBtn); - }); - - // Indicator should be gone and message back in input - expect( - screen.queryByTestId("queued-message-indicator"), - ).not.toBeInTheDocument(); - expect((input as HTMLTextAreaElement).value).toBe("Edit me back"); - }); - - it("subsequent submissions are appended to the queue (Bug 168)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - - // Send first message to start loading - await act(async () => { - fireEvent.change(input, { target: { value: "First" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Queue first message - await act(async () => { - fireEvent.change(input, { target: { value: "Queue 1" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - await screen.findByTestId("queued-message-indicator"); - - // Queue second message — should be appended, not overwrite the first - await act(async () => { - fireEvent.change(input, { target: { value: "Queue 2" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Both messages should be visible - const indicators = await screen.findAllByTestId("queued-message-indicator"); - expect(indicators).toHaveLength(2); - expect(indicators[0]).toHaveTextContent("Queue 1"); - expect(indicators[1]).toHaveTextContent("Queue 2"); - }); - - it("all queued messages are drained at once when agent responds (Story 199)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - - // Send first message to start loading - await act(async () => { - fireEvent.change(input, { target: { value: "First" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Queue two messages while loading - await act(async () => { - fireEvent.change(input, { target: { value: "Second" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - await act(async () => { - fireEvent.change(input, { target: { value: "Third" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Both messages should be visible in order - const indicators = await screen.findAllByTestId("queued-message-indicator"); - expect(indicators).toHaveLength(2); - expect(indicators[0]).toHaveTextContent("Second"); - expect(indicators[1]).toHaveTextContent("Third"); - - // Simulate first response completing — both "Second" and "Third" are drained at once - act(() => { - capturedWsHandlers?.onUpdate([ - { role: "user", content: "First" }, - { role: "assistant", content: "Response 1." }, - ]); - }); - - // Both queued indicators should be gone — entire queue drained in one shot - await waitFor(() => { - const remaining = screen.queryAllByTestId("queued-message-indicator"); - expect(remaining).toHaveLength(0); - }); - }); - - it("does not auto-send queued message when generation is cancelled (AC6)", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - - // Send first message to start loading - await act(async () => { - fireEvent.change(input, { target: { value: "First" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Queue a second message - await act(async () => { - fireEvent.change(input, { target: { value: "Should not send" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - await screen.findByTestId("queued-message-indicator"); - - // Click the stop button (■) — but input is empty so button is stop - // Actually simulate cancel by clicking the stop button (which requires empty input) - // We need to use the send button when input is empty (stop mode) - // Simulate cancel via the cancelGeneration path: the button when loading && !input - // At this point input is empty (was cleared after queuing) - const stopButton = screen.getByRole("button", { name: "■" }); - await act(async () => { - fireEvent.click(stopButton); - }); - - // Queued indicator should be gone (cancelled) - await waitFor(() => { - expect( - screen.queryByTestId("queued-message-indicator"), - ).not.toBeInTheDocument(); - }); - }); -}); - -describe("Remove bubble styling from streaming messages (Story 163)", () => { - beforeEach(() => { - capturedWsHandlers = null; - setupMocks(); - }); - - it("AC1: streaming assistant message uses transparent background, no extra padding, no border-radius", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Send a message to put chat into loading state - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Hello" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Simulate streaming tokens arriving - act(() => { - capturedWsHandlers?.onToken("Streaming response text"); - }); - - // Find the streaming message container (the inner div wrapping the Markdown) - const streamingText = await screen.findByText("Streaming response text"); - // The markdown-body wrapper is the parent, and the styled div is its parent - const styledDiv = streamingText.closest(".markdown-body") - ?.parentElement as HTMLElement; - - expect(styledDiv).toBeTruthy(); - const styleAttr = styledDiv.getAttribute("style") ?? ""; - expect(styleAttr).toContain("background: transparent"); - expect(styleAttr).toContain("padding: 0px"); - expect(styleAttr).toContain("border-radius: 0px"); - expect(styleAttr).toContain("max-width: 100%"); - }); - - it("AC1: streaming message wraps Markdown in markdown-body class", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Hello" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - act(() => { - capturedWsHandlers?.onToken("Some markdown content"); - }); - - const streamingText = await screen.findByText("Some markdown content"); - const markdownBody = streamingText.closest(".markdown-body"); - expect(markdownBody).toBeTruthy(); - }); - - it("AC2: no visual change when streaming ends and message transitions to completed", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Send a message to start streaming - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Hello" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - // Simulate streaming tokens - act(() => { - capturedWsHandlers?.onToken("Final response"); - }); - - // Capture streaming message style attribute - const streamingText = await screen.findByText("Final response"); - const streamingStyledDiv = streamingText.closest(".markdown-body") - ?.parentElement as HTMLElement; - const streamingStyleAttr = streamingStyledDiv.getAttribute("style") ?? ""; - - // Transition: onUpdate completes the message - act(() => { - capturedWsHandlers?.onUpdate([ - { role: "user", content: "Hello" }, - { role: "assistant", content: "Final response" }, - ]); - }); - - // Find the completed message — it should have the same styling - const completedText = await screen.findByText("Final response"); - const completedMarkdownBody = completedText.closest(".markdown-body"); - const completedStyledDiv = - completedMarkdownBody?.parentElement as HTMLElement; - - expect(completedStyledDiv).toBeTruthy(); - const completedStyleAttr = completedStyledDiv.getAttribute("style") ?? ""; - - // Both streaming and completed use transparent bg, 0 padding, 0 border-radius - expect(completedStyleAttr).toContain("background: transparent"); - expect(completedStyleAttr).toContain("padding: 0px"); - expect(completedStyleAttr).toContain("border-radius: 0px"); - expect(streamingStyleAttr).toContain("background: transparent"); - expect(streamingStyleAttr).toContain("padding: 0px"); - expect(streamingStyleAttr).toContain("border-radius: 0px"); - - // Both have the markdown-body class wrapper - expect(streamingStyledDiv.querySelector(".markdown-body")).toBeTruthy(); - }); - - it("AC3: completed assistant messages retain transparent background and no border-radius", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - act(() => { - capturedWsHandlers?.onUpdate([ - { role: "user", content: "Hi" }, - { role: "assistant", content: "Hello there!" }, - ]); - }); - - const assistantText = await screen.findByText("Hello there!"); - const markdownBody = assistantText.closest(".markdown-body"); - const styledDiv = markdownBody?.parentElement as HTMLElement; - - expect(styledDiv).toBeTruthy(); - const styleAttr = styledDiv.getAttribute("style") ?? ""; - expect(styleAttr).toContain("background: transparent"); - expect(styleAttr).toContain("padding: 0px"); - expect(styleAttr).toContain("border-radius: 0px"); - expect(styleAttr).toContain("max-width: 100%"); - }); - - it("AC3: completed user messages still have their bubble styling", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - act(() => { - capturedWsHandlers?.onUpdate([ - { role: "user", content: "I am a user message" }, - { role: "assistant", content: "I am a response" }, - ]); - }); - - // findByText finds the text element; traverse up to the styled bubble div - const userText = await screen.findByText("I am a user message"); - // User messages are rendered via markdown, so text is inside a

inside .user-markdown-body - // Walk up to find the styled bubble container - const bubbleDiv = userText.closest("[style*='padding: 10px 16px']"); - expect(bubbleDiv).toBeTruthy(); - const styleAttr = bubbleDiv?.getAttribute("style") ?? ""; - // User messages retain bubble: distinct background, padding, rounded corners - expect(styleAttr).toContain("padding: 10px 16px"); - expect(styleAttr).toContain("border-radius: 20px"); - expect(styleAttr).not.toContain("background: transparent"); - }); -}); - -describe("Bug 264: Claude Code session ID persisted across browser refresh", () => { - const PROJECT_PATH = "/tmp/project"; - const SESSION_KEY = `storykit-claude-session-id:${PROJECT_PATH}`; - const STORAGE_KEY = `storykit-chat-history:${PROJECT_PATH}`; - - beforeEach(() => { - capturedWsHandlers = null; - lastSendChatArgs = null; - localStorage.clear(); - setupMocks(); - }); - - afterEach(() => { - localStorage.clear(); - }); - - it("AC1: session_id is persisted to localStorage when onSessionId fires", async () => { - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - act(() => { - capturedWsHandlers?.onSessionId("test-session-abc"); - }); - - await waitFor(() => { - expect(localStorage.getItem(SESSION_KEY)).toBe("test-session-abc"); - }); - }); - - it("AC2: after remount, next sendChat includes session_id from localStorage", async () => { - // Step 1: Render, receive a session ID, then unmount (simulate refresh) - localStorage.setItem(SESSION_KEY, "persisted-session-xyz"); - localStorage.setItem( - STORAGE_KEY, - JSON.stringify([ - { role: "user", content: "Prior message" }, - { role: "assistant", content: "Prior reply" }, - ]), - ); - - const { unmount } = render( - , - ); - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - unmount(); - - // Step 2: Remount (simulates page reload) - capturedWsHandlers = null; - lastSendChatArgs = null; - render(); - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - // Prior messages should be visible - expect(await screen.findByText("Prior message")).toBeInTheDocument(); - - // Step 3: Send a new message — config should include session_id - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "Continue" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - expect(lastSendChatArgs).not.toBeNull(); - expect( - ( - ( - lastSendChatArgs as unknown as { - messages: Message[]; - config: unknown; - } - )?.config as Record - ).session_id, - ).toBe("persisted-session-xyz"); - }); - - it("AC3: clearing the session also clears the persisted session_id", async () => { - localStorage.setItem(SESSION_KEY, "session-to-clear"); - - const confirmSpy = vi.spyOn(window, "confirm").mockReturnValue(true); - - render(); - - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const newSessionBtn = screen.getByText(/New Session/); - await act(async () => { - fireEvent.click(newSessionBtn); - }); - - expect(localStorage.getItem(SESSION_KEY)).toBeNull(); - - confirmSpy.mockRestore(); - }); - - it("AC1: storage key is scoped to project path", async () => { - const otherPath = "/other/project"; - const otherKey = `storykit-claude-session-id:${otherPath}`; - localStorage.setItem(otherKey, "other-session"); - - render(); - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - act(() => { - capturedWsHandlers?.onSessionId("my-session"); - }); - - await waitFor(() => { - expect(localStorage.getItem(SESSION_KEY)).toBe("my-session"); - }); - - // Other project's session should be untouched - expect(localStorage.getItem(otherKey)).toBe("other-session"); - }); -}); - -describe("File reference expansion (Story 269 AC4)", () => { - beforeEach(() => { - vi.clearAllMocks(); - capturedWsHandlers = null; - lastSendChatArgs = null; - setupMocks(); - }); - - it("includes file contents as context when message contains @file reference", async () => { - mockedApi.readFile.mockResolvedValue('fn main() { println!("hello"); }'); - - render(); - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "explain @src/main.rs" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - await waitFor(() => expect(lastSendChatArgs).not.toBeNull()); - const sentMessages = ( - lastSendChatArgs as NonNullable - ).messages; - const userMsg = sentMessages[sentMessages.length - 1]; - expect(userMsg.content).toContain("explain @src/main.rs"); - expect(userMsg.content).toContain("[File: src/main.rs]"); - expect(userMsg.content).toContain("fn main()"); - }); - - it("sends message without modification when no @file references are present", async () => { - render(); - await waitFor(() => expect(capturedWsHandlers).not.toBeNull()); - - const input = screen.getByPlaceholderText("Send a message..."); - await act(async () => { - fireEvent.change(input, { target: { value: "hello world" } }); - }); - await act(async () => { - fireEvent.keyDown(input, { key: "Enter", shiftKey: false }); - }); - - await waitFor(() => expect(lastSendChatArgs).not.toBeNull()); - const sentMessages = ( - lastSendChatArgs as NonNullable - ).messages; - const userMsg = sentMessages[sentMessages.length - 1]; - expect(userMsg.content).toBe("hello world"); - expect(mockedApi.readFile).not.toHaveBeenCalled(); - }); -}); diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx deleted file mode 100644 index 74f4113..0000000 --- a/frontend/src/components/Chat.tsx +++ /dev/null @@ -1,1398 +0,0 @@ -import * as React from "react"; -import Markdown from "react-markdown"; -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"; -import type { AgentConfigInfo } from "../api/agents"; -import { agentsApi } from "../api/agents"; -import type { AnthropicModelInfo, PipelineState } from "../api/client"; -import { api, ChatWebSocket } from "../api/client"; -import { useChatHistory } from "../hooks/useChatHistory"; -import type { Message, ProviderConfig } from "../types"; -import { AgentPanel } from "./AgentPanel"; -import { ChatHeader } from "./ChatHeader"; -import type { ChatInputHandle } from "./ChatInput"; -import { ChatInput } from "./ChatInput"; -import { HelpOverlay } from "./HelpOverlay"; -import { LozengeFlyProvider } from "./LozengeFlyContext"; -import { MessageItem } from "./MessageItem"; -import type { LogEntry } from "./ServerLogsPanel"; -import { ServerLogsPanel } from "./ServerLogsPanel"; -import { SideQuestionOverlay } from "./SideQuestionOverlay"; -import { StagePanel } from "./StagePanel"; -import { WorkItemDetailPanel } from "./WorkItemDetailPanel"; - -const { useCallback, useEffect, useMemo, useRef, useState } = React; - -/** Fixed-height thinking trace block that auto-scrolls to bottom as text arrives. */ -function ThinkingBlock({ text }: { text: string }) { - const scrollRef = useRef(null); - - useEffect(() => { - const el = scrollRef.current; - if (el) { - el.scrollTop = el.scrollHeight; - } - }, [text]); - - return ( -

- - thinking - - {text} -
- ); -} - -/** Streaming message renderer — stable component to avoid recreation on each render. */ -function StreamingMessage({ content }: { content: string }) { - return ( - { - const match = /language-(\w+)/.exec(className || ""); - const isInline = !className; - return !isInline && match ? ( - - {String(children).replace(/\n$/, "")} - - ) : ( - - {children} - - ); - }, - }} - > - {content} - - ); -} - -const NARROW_BREAKPOINT = 900; - -function formatToolActivity(toolName: string): string { - switch (toolName) { - // Built-in provider tool names - case "read_file": - case "Read": - return "Reading file..."; - case "write_file": - case "Write": - case "Edit": - return "Writing file..."; - case "list_directory": - case "Glob": - return "Listing files..."; - case "search_files": - case "Grep": - return "Searching files..."; - case "exec_shell": - case "Bash": - return "Executing command..."; - // Claude Code additional tool names - case "Task": - return "Running task..."; - case "WebFetch": - return "Fetching web content..."; - case "WebSearch": - return "Searching the web..."; - case "NotebookEdit": - return "Editing notebook..."; - case "TodoWrite": - return "Updating tasks..."; - default: - return `Using ${toolName}...`; - } -} - -const estimateTokens = (text: string): number => Math.ceil(text.length / 4); - -const getContextWindowSize = ( - modelName: string, - claudeContextWindows?: Map, -): number => { - if (modelName.startsWith("claude-")) { - return claudeContextWindows?.get(modelName) ?? 200000; - } - if (modelName.includes("llama3")) return 8192; - if (modelName.includes("qwen2.5")) return 32768; - if (modelName.includes("deepseek")) return 16384; - return 8192; -}; - -interface ChatProps { - projectPath: string; - onCloseProject: () => void; -} - -export function Chat({ projectPath, onCloseProject }: ChatProps) { - const { messages, setMessages, clearMessages } = useChatHistory(projectPath); - const [loading, setLoading] = useState(false); - const [model, setModel] = useState("claude-code-pty"); - const [enableTools, setEnableTools] = useState(true); - const [availableModels, setAvailableModels] = useState([]); - const [claudeModels, setClaudeModels] = useState([]); - const [claudeContextWindowMap, setClaudeContextWindowMap] = useState< - Map - >(new Map()); - const [streamingContent, setStreamingContent] = useState(""); - const [streamingThinking, setStreamingThinking] = useState(""); - const [showApiKeyDialog, setShowApiKeyDialog] = useState(false); - const [apiKeyInput, setApiKeyInput] = useState(""); - const [hasAnthropicKey, setHasAnthropicKey] = useState(false); - const [pipeline, setPipeline] = useState({ - backlog: [], - current: [], - qa: [], - merge: [], - done: [], - }); - const [claudeSessionId, setClaudeSessionId] = useState(() => { - try { - return ( - localStorage.getItem(`storykit-claude-session-id:${projectPath}`) ?? - null - ); - } catch { - return null; - } - }); - const [activityStatus, setActivityStatus] = useState(null); - const [permissionQueue, setPermissionQueue] = useState< - { - requestId: string; - toolName: string; - toolInput: Record; - }[] - >([]); - const [isNarrowScreen, setIsNarrowScreen] = useState( - window.innerWidth < NARROW_BREAKPOINT, - ); - const [reconciliationActive, setReconciliationActive] = useState(false); - const [reconciliationEvents, setReconciliationEvents] = useState< - { id: string; storyId: string; status: string; message: string }[] - >([]); - const reconciliationEventIdRef = useRef(0); - const [agentConfigVersion, setAgentConfigVersion] = useState(0); - const [agentStateVersion, setAgentStateVersion] = useState(0); - const [pipelineVersion, setPipelineVersion] = useState(0); - const [agentRoster, setAgentRoster] = useState([]); - const [storyTokenCosts, setStoryTokenCosts] = useState>( - new Map(), - ); - const [needsOnboarding, setNeedsOnboarding] = useState(false); - const onboardingTriggeredRef = useRef(false); - const [selectedWorkItemId, setSelectedWorkItemId] = useState( - null, - ); - - const handleDeleteItem = React.useCallback( - (item: import("../api/client").PipelineStageItem) => { - api.deleteStory(item.story_id).catch((err: unknown) => { - console.error("Failed to delete story:", err); - }); - }, - [], - ); - const [queuedMessages, setQueuedMessages] = useState< - { id: string; text: string }[] - >([]); - const [sideQuestion, setSideQuestion] = useState<{ - question: string; - response: string; - loading: boolean; - } | null>(null); - const [showHelp, setShowHelp] = useState(false); - const [serverLogs, setServerLogs] = useState([]); - const [wsConnected, setWsConnected] = useState(false); - // Ref so stale WebSocket callbacks can read the current queued messages - const queuedMessagesRef = useRef<{ id: string; text: string }[]>([]); - const queueIdCounterRef = useRef(0); - // Trigger state: set to a batch of message strings to fire auto-send after loading ends - const [pendingAutoSendBatch, setPendingAutoSendBatch] = useState< - string[] | null - >(null); - - const wsRef = useRef(null); - const chatInputRef = useRef(null); - const messagesEndRef = useRef(null); - const scrollContainerRef = useRef(null); - const shouldAutoScrollRef = useRef(true); - const lastScrollTopRef = useRef(0); - const userScrolledUpRef = useRef(false); - const pendingMessageRef = useRef(""); - - // Agents currently running or pending across all pipeline stages. - const busyAgentNames = useMemo(() => { - const busy = new Set(); - const allItems = [ - ...pipeline.backlog, - ...pipeline.current, - ...pipeline.qa, - ...pipeline.merge, - ]; - for (const item of allItems) { - if ( - item.agent && - (item.agent.status === "running" || item.agent.status === "pending") - ) { - busy.add(item.agent.agent_name); - } - } - return busy; - }, [pipeline]); - - const contextUsage = useMemo(() => { - let totalTokens = 0; - totalTokens += 200; - - for (const msg of messages) { - totalTokens += estimateTokens(msg.content); - if (msg.tool_calls) { - totalTokens += estimateTokens(JSON.stringify(msg.tool_calls)); - } - } - - if (streamingContent) { - totalTokens += estimateTokens(streamingContent); - } - - const contextWindow = getContextWindowSize(model, claudeContextWindowMap); - const percentage = Math.round((totalTokens / contextWindow) * 100); - - return { - used: totalTokens, - total: contextWindow, - percentage, - }; - }, [messages, streamingContent, model, claudeContextWindowMap]); - - useEffect(() => { - try { - if (claudeSessionId !== null) { - localStorage.setItem( - `storykit-claude-session-id:${projectPath}`, - claudeSessionId, - ); - } else { - localStorage.removeItem(`storykit-claude-session-id:${projectPath}`); - } - } catch { - // Ignore — quota or security errors. - } - }, [claudeSessionId, projectPath]); - - useEffect(() => { - api - .getOllamaModels() - .then(async (models) => { - if (models.length > 0) { - const sortedModels = models.sort((a, b) => - a.toLowerCase().localeCompare(b.toLowerCase()), - ); - setAvailableModels(sortedModels); - - try { - const savedModel = await api.getModelPreference(); - if (savedModel) { - setModel(savedModel); - } - } catch (e) { - console.error(e); - } - } - }) - .catch((err) => console.error(err)); - - api - .getAnthropicApiKeyExists() - .then((exists) => { - setHasAnthropicKey(exists); - if (!exists) return; - return api.getAnthropicModels().then((models: AnthropicModelInfo[]) => { - if (models.length > 0) { - const sortedModels = models.sort((a, b) => - a.id.toLowerCase().localeCompare(b.id.toLowerCase()), - ); - setClaudeModels(sortedModels.map((m) => m.id)); - setClaudeContextWindowMap( - new Map(sortedModels.map((m) => [m.id, m.context_window])), - ); - } else { - setClaudeModels([]); - setClaudeContextWindowMap(new Map()); - } - }); - }) - .catch((err) => { - console.error(err); - setHasAnthropicKey(false); - setClaudeModels([]); - }); - }, []); - - useEffect(() => { - const ws = new ChatWebSocket(); - wsRef.current = ws; - - ws.connect({ - onToken: (content) => { - setStreamingContent((prev: string) => prev + content); - }, - onThinkingToken: (content) => { - setStreamingThinking((prev: string) => prev + content); - }, - onUpdate: (history) => { - setMessages(history); - setStreamingContent(""); - setStreamingThinking(""); - const last = history[history.length - 1]; - if (last?.role === "assistant" && !last.tool_calls) { - setLoading(false); - setActivityStatus(null); - if (queuedMessagesRef.current.length > 0) { - const batch = queuedMessagesRef.current.map((item) => item.text); - queuedMessagesRef.current = []; - setQueuedMessages([]); - setPendingAutoSendBatch(batch); - } - } - }, - onSessionId: (sessionId) => { - setClaudeSessionId(sessionId); - }, - onError: (message) => { - console.error("WebSocket error:", message); - setLoading(false); - setActivityStatus(null); - if (queuedMessagesRef.current.length > 0) { - const batch = queuedMessagesRef.current.map((item) => item.text); - queuedMessagesRef.current = []; - setQueuedMessages([]); - setPendingAutoSendBatch(batch); - } - }, - onPipelineState: (state) => { - setPipeline(state); - setPipelineVersion((v) => v + 1); - const allItems = [ - ...state.backlog, - ...state.current, - ...state.qa, - ...state.merge, - ...state.done, - ]; - for (const item of allItems) { - api - .getTokenCost(item.story_id) - .then((cost) => { - if (cost.total_cost_usd > 0) { - setStoryTokenCosts((prev) => { - const next = new Map(prev); - next.set(item.story_id, cost.total_cost_usd); - return next; - }); - } - }) - .catch(() => { - // Silently ignore — cost data may not exist yet. - }); - } - }, - onPermissionRequest: (requestId, toolName, toolInput) => { - setPermissionQueue((prev) => [ - ...prev, - { requestId, toolName, toolInput }, - ]); - }, - onActivity: (toolName) => { - setActivityStatus(formatToolActivity(toolName)); - }, - onReconciliationProgress: (storyId, status, message) => { - if (status === "done") { - setReconciliationActive(false); - } else { - setReconciliationActive(true); - setReconciliationEvents((prev) => { - const id = String(reconciliationEventIdRef.current++); - const next = [...prev, { id, storyId, status, message }]; - // Keep only the last 8 events to avoid the banner growing too tall. - return next.slice(-8); - }); - } - }, - onAgentConfigChanged: () => { - setAgentConfigVersion((v) => v + 1); - }, - onAgentStateChanged: () => { - setAgentStateVersion((v) => v + 1); - }, - onOnboardingStatus: (onboarding: boolean) => { - setNeedsOnboarding(onboarding); - }, - onSideQuestionToken: (content) => { - setSideQuestion((prev) => - prev ? { ...prev, response: prev.response + content } : prev, - ); - }, - onSideQuestionDone: (response) => { - setSideQuestion((prev) => - prev ? { ...prev, response, loading: false } : prev, - ); - }, - onLogEntry: (timestamp, level, message) => { - setServerLogs((prev) => [...prev, { timestamp, level, message }]); - }, - onConnected: () => { - setWsConnected(true); - }, - }); - - return () => { - ws.close(); - wsRef.current = null; - }; - }, []); - - const scrollToBottom = useCallback(() => { - const element = scrollContainerRef.current; - if (element) { - element.scrollTop = element.scrollHeight; - // Read scrollTop back after assignment: the browser caps it at - // (scrollHeight - clientHeight), so storing scrollHeight would - // make handleScroll incorrectly interpret the next scroll event - // as an upward scroll and disable auto-scrolling. - lastScrollTopRef.current = element.scrollTop; - } - }, []); - - const handleScroll = () => { - const element = scrollContainerRef.current; - if (!element) return; - - const currentScrollTop = element.scrollTop; - const isAtBottom = - element.scrollHeight - element.scrollTop - element.clientHeight < 5; - - if (currentScrollTop < lastScrollTopRef.current) { - userScrolledUpRef.current = true; - shouldAutoScrollRef.current = false; - } - - if (isAtBottom) { - userScrolledUpRef.current = false; - shouldAutoScrollRef.current = true; - } - - lastScrollTopRef.current = currentScrollTop; - }; - - const autoScrollKey = - messages.length + streamingContent.length + streamingThinking.length; - - useEffect(() => { - if ( - autoScrollKey >= 0 && - shouldAutoScrollRef.current && - !userScrolledUpRef.current - ) { - scrollToBottom(); - } - }, [autoScrollKey, scrollToBottom]); - - // Auto-send all queued messages as a batch when loading ends - useEffect(() => { - if (pendingAutoSendBatch && pendingAutoSendBatch.length > 0) { - const batch = pendingAutoSendBatch; - setPendingAutoSendBatch(null); - sendMessageBatch(batch); - } - }, [pendingAutoSendBatch]); - - useEffect(() => { - const handleResize = () => - setIsNarrowScreen(window.innerWidth < NARROW_BREAKPOINT); - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, []); - - // Fetch agent roster whenever the config changes so the start button knows available agents. - useEffect(() => { - agentsApi - .getAgentConfig() - .then(setAgentRoster) - .catch(() => { - // Silently ignore — roster unavailable. - }); - }, [agentConfigVersion]); - - const handleStartAgent = useCallback( - async (storyId: string, agentName?: string) => { - try { - await agentsApi.startAgent(storyId, agentName); - } catch (err) { - console.error("Failed to start agent:", err); - } - }, - [], - ); - - const cancelGeneration = async () => { - // Preserve queued messages by appending them to the chat input box - if (queuedMessagesRef.current.length > 0) { - const queued = queuedMessagesRef.current - .map((item) => item.text) - .join("\n"); - chatInputRef.current?.appendToInput(queued); - } - queuedMessagesRef.current = []; - setQueuedMessages([]); - try { - wsRef.current?.cancel(); - await api.cancelChat(); - - if (streamingContent) { - setMessages((prev: Message[]) => [ - ...prev, - { role: "assistant", content: streamingContent }, - ]); - setStreamingContent(""); - } - - setStreamingThinking(""); - setLoading(false); - setActivityStatus(null); - } catch (e) { - console.error("Failed to cancel chat:", e); - } - }; - - const sendMessage = async (messageText: string) => { - if (!messageText.trim()) return; - - // /help — show available slash commands overlay - if (/^\/help\s*$/i.test(messageText)) { - setShowHelp(true); - return; - } - - // /btw — answered from context without disrupting main chat - const btwMatch = messageText.match(/^\/btw\s+(.+)/s); - if (btwMatch) { - const question = btwMatch[1].trim(); - setSideQuestion({ question, response: "", loading: true }); - - const isClaudeCode = model === "claude-code-pty"; - const provider = isClaudeCode - ? "claude-code" - : model.startsWith("claude-") - ? "anthropic" - : "ollama"; - const config: ProviderConfig = { - provider, - model, - base_url: "http://localhost:11434", - enable_tools: false, - }; - wsRef.current?.sendSideQuestion(question, messages, config); - return; - } - - // Agent is busy — queue the message instead of dropping it - if (loading) { - const newItem = { - id: String(queueIdCounterRef.current++), - text: messageText, - }; - queuedMessagesRef.current = [...queuedMessagesRef.current, newItem]; - setQueuedMessages([...queuedMessagesRef.current]); - return; - } - - const isClaudeCode = model === "claude-code-pty"; - if (!isClaudeCode && model.startsWith("claude-")) { - const hasKey = await api.getAnthropicApiKeyExists(); - if (!hasKey) { - pendingMessageRef.current = messageText; - setShowApiKeyDialog(true); - return; - } - } - - // Expand @file references: append file contents as context - const fileRefs = [...messageText.matchAll(/(^|[\s\n])@([^\s@]+)/g)].map( - (m) => m[2], - ); - let expandedText = messageText; - if (fileRefs.length > 0) { - const expansions = await Promise.allSettled( - fileRefs.map(async (ref) => { - const contents = await api.readFile(ref); - return { ref, contents }; - }), - ); - for (const result of expansions) { - if (result.status === "fulfilled") { - expandedText += `\n\n[File: ${result.value.ref}]\n\`\`\`\n${result.value.contents}\n\`\`\``; - } - } - } - - const userMsg: Message = { role: "user", content: expandedText }; - const newHistory = [...messages, userMsg]; - - setMessages(newHistory); - setLoading(true); - setStreamingContent(""); - setStreamingThinking(""); - setActivityStatus(null); - - try { - const provider = isClaudeCode - ? "claude-code" - : model.startsWith("claude-") - ? "anthropic" - : "ollama"; - const config: ProviderConfig = { - provider, - model, - base_url: "http://localhost:11434", - enable_tools: enableTools, - ...(isClaudeCode && claudeSessionId - ? { session_id: claudeSessionId } - : {}), - }; - - wsRef.current?.sendChat(newHistory, config); - } catch (e) { - console.error("Chat error:", e); - const errorMessage = String(e); - if (!errorMessage.includes("Chat cancelled by user")) { - setMessages((prev: Message[]) => [ - ...prev, - { role: "assistant", content: `**Error:** ${e}` }, - ]); - } - setLoading(false); - } - }; - - const sendMessageBatch = async (messageTexts: string[]) => { - if (messageTexts.length === 0) return; - - const userMsgs: Message[] = messageTexts.map((text) => ({ - role: "user", - content: text, - })); - const newHistory = [...messages, ...userMsgs]; - - setMessages(newHistory); - setLoading(true); - setStreamingContent(""); - setStreamingThinking(""); - setActivityStatus(null); - - try { - const isClaudeCode = model === "claude-code-pty"; - const provider = isClaudeCode - ? "claude-code" - : model.startsWith("claude-") - ? "anthropic" - : "ollama"; - const config: ProviderConfig = { - provider, - model, - base_url: "http://localhost:11434", - enable_tools: enableTools, - ...(isClaudeCode && claudeSessionId - ? { session_id: claudeSessionId } - : {}), - }; - wsRef.current?.sendChat(newHistory, config); - } catch (e) { - console.error("Chat error:", e); - const errorMessage = String(e); - if (!errorMessage.includes("Chat cancelled by user")) { - setMessages((prev: Message[]) => [ - ...prev, - { role: "assistant", content: `**Error:** ${e}` }, - ]); - } - setLoading(false); - } - }; - - const handleSaveApiKey = async () => { - if (!apiKeyInput.trim()) return; - - try { - await api.setAnthropicApiKey(apiKeyInput); - setShowApiKeyDialog(false); - setApiKeyInput(""); - - const pendingMessage = pendingMessageRef.current; - pendingMessageRef.current = ""; - - if (pendingMessage.trim()) { - sendMessage(pendingMessage); - } - } catch (e) { - console.error("Failed to save API key:", e); - alert(`Failed to save API key: ${e}`); - } - }; - - const handlePermissionResponse = (approved: boolean, alwaysAllow = false) => { - const current = permissionQueue[0]; - if (!current) return; - wsRef.current?.sendPermissionResponse( - current.requestId, - approved, - alwaysAllow, - ); - setPermissionQueue((prev) => prev.slice(1)); - }; - - const clearSession = async () => { - const confirmed = window.confirm( - "Are you sure? This will clear all messages and reset the conversation context.", - ); - - if (confirmed) { - try { - await api.cancelChat(); - wsRef.current?.cancel(); - } catch (e) { - console.error("Failed to cancel chat:", e); - } - - clearMessages(); - setStreamingContent(""); - setStreamingThinking(""); - setLoading(false); - setActivityStatus(null); - setClaudeSessionId(null); - try { - localStorage.removeItem(`storykit-claude-session-id:${projectPath}`); - } catch { - // Ignore — quota or security errors. - } - } - }; - - const handleRemoveQueuedMessage = useCallback((id: string) => { - queuedMessagesRef.current = queuedMessagesRef.current.filter( - (item) => item.id !== id, - ); - setQueuedMessages([...queuedMessagesRef.current]); - }, []); - - const handleStopAgent = useCallback((storyId: string, agentName: string) => { - agentsApi.stopAgent(storyId, agentName).catch((err: unknown) => { - console.error("Failed to stop agent:", err); - }); - }, []); - - return ( -
- { - setModel(newModel); - api.setModelPreference(newModel).catch(console.error); - }} - enableTools={enableTools} - onToggleTools={setEnableTools} - wsConnected={wsConnected} - /> - - {/* Two-column content area */} -
- {/* Left column: chat messages + input pinned at bottom */} -
- {/* Scrollable messages area */} -
-
- {needsOnboarding && messages.length === 0 && !loading && ( -
-

- Welcome to Storkit -

-

- This project needs to be set up before you can start writing - stories. The agent will guide you through configuring your - project goals and tech stack. -

- -
- )} - {messages.map((msg: Message, idx: number) => ( - - ))} - {loading && streamingThinking && ( - - )} - {loading && streamingContent && ( -
-
-
- -
-
-
- )} - {loading && - (activityStatus != null || - (!streamingContent && !streamingThinking)) && ( -
- - {activityStatus ?? "Thinking..."} - -
- )} -
-
-
- - {/* Startup reconciliation progress banner */} - {reconciliationActive && ( -
-
- Reconciling startup state... -
- {reconciliationEvents.map((evt) => ( -
- {evt.storyId ? `[${evt.storyId}] ` : ""} - {evt.message} -
- ))} -
- )} - - {/* Chat input pinned at bottom of left column */} - -
- - {/* Right column: panels independently scrollable */} -
- - {selectedWorkItemId ? ( - setSelectedWorkItemId(null)} - /> - ) : ( - <> - - - setSelectedWorkItemId(item.story_id)} - onStopAgent={handleStopAgent} - onDeleteItem={handleDeleteItem} - /> - setSelectedWorkItemId(item.story_id)} - onStopAgent={handleStopAgent} - onDeleteItem={handleDeleteItem} - /> - setSelectedWorkItemId(item.story_id)} - onStopAgent={handleStopAgent} - onDeleteItem={handleDeleteItem} - /> - setSelectedWorkItemId(item.story_id)} - agentRoster={agentRoster} - busyAgentNames={busyAgentNames} - onStartAgent={handleStartAgent} - onStopAgent={handleStopAgent} - onDeleteItem={handleDeleteItem} - /> - setSelectedWorkItemId(item.story_id)} - agentRoster={agentRoster} - busyAgentNames={busyAgentNames} - onStartAgent={handleStartAgent} - onStopAgent={handleStopAgent} - onDeleteItem={handleDeleteItem} - /> - - - )} - -
-
- - {showApiKeyDialog && ( -
-
-

- Enter Anthropic API Key -

-

- To use Claude models, please enter your Anthropic API key. Your - key will be stored server-side and reused across sessions. -

- setApiKeyInput(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && handleSaveApiKey()} - placeholder="sk-ant-..." - style={{ - width: "100%", - padding: "12px", - borderRadius: "8px", - border: "1px solid #555", - backgroundColor: "#1a1a1a", - color: "#ececec", - fontSize: "1em", - marginBottom: "20px", - outline: "none", - }} - /> -
- - -
-
-
- )} - {permissionQueue.length > 0 && ( -
-
-

- Permission Request - {permissionQueue.length > 1 && ( - - (+{permissionQueue.length - 1} queued) - - )} -

-

- The agent wants to use the{" "} - - {permissionQueue[0].toolName} - {" "} - tool. Do you approve? -

- {Object.keys(permissionQueue[0].toolInput).length > 0 && ( -
-								{JSON.stringify(permissionQueue[0].toolInput, null, 2)}
-							
- )} -
- - - -
-
-
- )} - - {showHelp && setShowHelp(false)} />} - - {sideQuestion && ( - setSideQuestion(null)} - /> - )} -
- ); -} diff --git a/frontend/src/components/ChatHeader.test.tsx b/frontend/src/components/ChatHeader.test.tsx deleted file mode 100644 index 814858e..0000000 --- a/frontend/src/components/ChatHeader.test.tsx +++ /dev/null @@ -1,314 +0,0 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import { describe, expect, it, vi } from "vitest"; -import { ChatHeader } from "./ChatHeader"; - -vi.mock("../api/client", () => ({ - api: { - rebuildAndRestart: vi.fn(), - }, -})); - -interface ChatHeaderProps { - projectPath: string; - onCloseProject: () => void; - contextUsage: { used: number; total: number; percentage: number }; - onClearSession: () => void; - model: string; - availableModels: string[]; - claudeModels: string[]; - hasAnthropicKey: boolean; - onModelChange: (model: string) => void; - enableTools: boolean; - onToggleTools: (enabled: boolean) => void; - wsConnected: boolean; -} - -function makeProps(overrides: Partial = {}): ChatHeaderProps { - return { - projectPath: "/test/project", - onCloseProject: vi.fn(), - contextUsage: { used: 1000, total: 10000, percentage: 10 }, - onClearSession: vi.fn(), - model: "claude-sonnet", - availableModels: ["llama3"], - claudeModels: ["claude-sonnet"], - hasAnthropicKey: true, - onModelChange: vi.fn(), - enableTools: true, - onToggleTools: vi.fn(), - wsConnected: false, - ...overrides, - }; -} - -describe("ChatHeader", () => { - it("renders project path", () => { - render(); - expect(screen.getByText("/test/project")).toBeInTheDocument(); - }); - - it("calls onCloseProject when close button is clicked", () => { - const onCloseProject = vi.fn(); - render(); - fireEvent.click(screen.getByText("\u2715")); - expect(onCloseProject).toHaveBeenCalled(); - }); - - it("displays context percentage with green emoji when low", () => { - render( - , - ); - expect(screen.getByText(/10%/)).toBeInTheDocument(); - }); - - it("displays yellow emoji when context is 75-89%", () => { - render( - , - ); - expect(screen.getByText(/80%/)).toBeInTheDocument(); - }); - - it("displays red emoji when context is 90%+", () => { - render( - , - ); - expect(screen.getByText(/95%/)).toBeInTheDocument(); - }); - - it("calls onClearSession when New Session button is clicked", () => { - const onClearSession = vi.fn(); - render(); - fireEvent.click(screen.getByText(/New Session/)); - expect(onClearSession).toHaveBeenCalled(); - }); - - it("renders select dropdown when model options are available", () => { - render(); - const select = screen.getByRole("combobox"); - expect(select).toBeInTheDocument(); - }); - - it("renders text input when no model options are available", () => { - render( - , - ); - expect(screen.getByPlaceholderText("Model")).toBeInTheDocument(); - }); - - it("calls onModelChange when model is selected from dropdown", () => { - const onModelChange = vi.fn(); - render(); - const select = screen.getByRole("combobox"); - fireEvent.change(select, { target: { value: "llama3" } }); - expect(onModelChange).toHaveBeenCalledWith("llama3"); - }); - - it("calls onModelChange when text is typed in model input", () => { - const onModelChange = vi.fn(); - render( - , - ); - const input = screen.getByPlaceholderText("Model"); - fireEvent.change(input, { target: { value: "custom-model" } }); - expect(onModelChange).toHaveBeenCalledWith("custom-model"); - }); - - it("calls onToggleTools when checkbox is toggled", () => { - const onToggleTools = vi.fn(); - render(); - const checkbox = screen.getByRole("checkbox"); - fireEvent.click(checkbox); - expect(onToggleTools).toHaveBeenCalled(); - }); - - it("displays the build timestamp in human-readable format", () => { - render(); - expect(screen.getByText("Built: 2026-01-01 00:00")).toBeInTheDocument(); - }); - - it("displays Storkit branding in the header", () => { - render(); - expect(screen.getByText("Storkit")).toBeInTheDocument(); - }); - - it("labels the claude-pty optgroup as 'Claude Code'", () => { - render(); - const optgroup = document.querySelector('optgroup[label="Claude Code"]'); - expect(optgroup).toBeInTheDocument(); - }); - - it("labels the Anthropic API optgroup as 'Anthropic API'", () => { - render(); - const optgroup = document.querySelector('optgroup[label="Anthropic API"]'); - expect(optgroup).toBeInTheDocument(); - }); - - it("shows disabled placeholder when claudeModels is empty and no API key", () => { - render( - , - ); - expect( - screen.getByText("Add Anthropic API key to load models"), - ).toBeInTheDocument(); - }); - - // ── Close button hover/focus handlers ───────────────────────────────────── - - it("close button changes background on mouseOver and resets on mouseOut", () => { - render(); - const closeBtn = screen.getByText("\u2715"); - fireEvent.mouseOver(closeBtn); - expect(closeBtn.style.background).toBe("rgb(51, 51, 51)"); - fireEvent.mouseOut(closeBtn); - expect(closeBtn.style.background).toBe("transparent"); - }); - - it("close button changes background on focus and resets on blur", () => { - render(); - const closeBtn = screen.getByText("\u2715"); - fireEvent.focus(closeBtn); - expect(closeBtn.style.background).toBe("rgb(51, 51, 51)"); - fireEvent.blur(closeBtn); - expect(closeBtn.style.background).toBe("transparent"); - }); - - // ── New Session button hover/focus handlers ─────────────────────────────── - - it("New Session button changes style on mouseOver and resets on mouseOut", () => { - render(); - const sessionBtn = screen.getByText(/New Session/); - fireEvent.mouseOver(sessionBtn); - expect(sessionBtn.style.backgroundColor).toBe("rgb(63, 63, 63)"); - expect(sessionBtn.style.color).toBe("rgb(204, 204, 204)"); - fireEvent.mouseOut(sessionBtn); - expect(sessionBtn.style.backgroundColor).toBe("rgb(47, 47, 47)"); - expect(sessionBtn.style.color).toBe("rgb(136, 136, 136)"); - }); - - it("New Session button changes style on focus and resets on blur", () => { - render(); - const sessionBtn = screen.getByText(/New Session/); - fireEvent.focus(sessionBtn); - expect(sessionBtn.style.backgroundColor).toBe("rgb(63, 63, 63)"); - expect(sessionBtn.style.color).toBe("rgb(204, 204, 204)"); - fireEvent.blur(sessionBtn); - expect(sessionBtn.style.backgroundColor).toBe("rgb(47, 47, 47)"); - expect(sessionBtn.style.color).toBe("rgb(136, 136, 136)"); - }); - - // ── Rebuild button ──────────────────────────────────────────────────────── - - it("renders rebuild button", () => { - render(); - expect( - screen.getByTitle("Rebuild and restart the server"), - ).toBeInTheDocument(); - }); - - it("shows confirmation dialog when rebuild button is clicked", () => { - render(); - fireEvent.click(screen.getByTitle("Rebuild and restart the server")); - expect(screen.getByText("Rebuild and restart?")).toBeInTheDocument(); - }); - - it("hides confirmation dialog when cancel is clicked", () => { - render(); - fireEvent.click(screen.getByTitle("Rebuild and restart the server")); - fireEvent.click(screen.getByText("Cancel")); - expect(screen.queryByText("Rebuild and restart?")).not.toBeInTheDocument(); - }); - - it("calls api.rebuildAndRestart and shows Building... when confirmed", async () => { - const { api } = await import("../api/client"); - vi.mocked(api.rebuildAndRestart).mockReturnValue(new Promise(() => {})); - - render(); - fireEvent.click(screen.getByTitle("Rebuild and restart the server")); - fireEvent.click(screen.getByText("Rebuild")); - - await waitFor(() => { - expect(screen.getByText("Building...")).toBeInTheDocument(); - }); - expect(api.rebuildAndRestart).toHaveBeenCalled(); - }); - - it("shows Reconnecting... when rebuild triggers a network error", async () => { - const { api } = await import("../api/client"); - vi.mocked(api.rebuildAndRestart).mockRejectedValue( - new TypeError("Failed to fetch"), - ); - - render(); - fireEvent.click(screen.getByTitle("Rebuild and restart the server")); - fireEvent.click(screen.getByText("Rebuild")); - - await waitFor(() => { - expect(screen.getByText("Reconnecting...")).toBeInTheDocument(); - }); - }); - - it("shows error when rebuild returns a failure message", async () => { - const { api } = await import("../api/client"); - vi.mocked(api.rebuildAndRestart).mockResolvedValue( - "error[E0308]: mismatched types", - ); - - render(); - fireEvent.click(screen.getByTitle("Rebuild and restart the server")); - fireEvent.click(screen.getByText("Rebuild")); - - await waitFor(() => { - expect(screen.getByText("⚠ Rebuild failed")).toBeInTheDocument(); - expect( - screen.getByText("error[E0308]: mismatched types"), - ).toBeInTheDocument(); - }); - }); - - it("clears reconnecting state when wsConnected transitions to true", async () => { - const { api } = await import("../api/client"); - vi.mocked(api.rebuildAndRestart).mockRejectedValue( - new TypeError("Failed to fetch"), - ); - - const { rerender } = render( - , - ); - fireEvent.click(screen.getByTitle("Rebuild and restart the server")); - fireEvent.click(screen.getByText("Rebuild")); - - await waitFor(() => { - expect(screen.getByText("Reconnecting...")).toBeInTheDocument(); - }); - - rerender(); - - await waitFor(() => { - expect(screen.getByText("↺ Rebuild")).toBeInTheDocument(); - }); - }); -}); diff --git a/frontend/src/components/ChatHeader.tsx b/frontend/src/components/ChatHeader.tsx deleted file mode 100644 index 5bed248..0000000 --- a/frontend/src/components/ChatHeader.tsx +++ /dev/null @@ -1,545 +0,0 @@ -import * as React from "react"; -import { api } from "../api/client"; - -const { useState, useEffect } = React; - -function formatBuildTime(isoString: string): string { - const d = new Date(isoString); - const year = d.getUTCFullYear(); - const month = String(d.getUTCMonth() + 1).padStart(2, "0"); - const day = String(d.getUTCDate()).padStart(2, "0"); - const hours = String(d.getUTCHours()).padStart(2, "0"); - const minutes = String(d.getUTCMinutes()).padStart(2, "0"); - return `Built: ${year}-${month}-${day} ${hours}:${minutes}`; -} - -interface ContextUsage { - used: number; - total: number; - percentage: number; -} - -interface ChatHeaderProps { - projectPath: string; - onCloseProject: () => void; - contextUsage: ContextUsage; - onClearSession: () => void; - model: string; - availableModels: string[]; - claudeModels: string[]; - hasAnthropicKey: boolean; - onModelChange: (model: string) => void; - enableTools: boolean; - onToggleTools: (enabled: boolean) => void; - wsConnected: boolean; -} - -const getContextEmoji = (percentage: number): string => { - if (percentage >= 90) return "🔴"; - if (percentage >= 75) return "🟡"; - return "🟢"; -}; - -type RebuildStatus = "idle" | "building" | "reconnecting" | "error"; - -export function ChatHeader({ - projectPath, - onCloseProject, - contextUsage, - onClearSession, - model, - availableModels, - claudeModels, - hasAnthropicKey, - onModelChange, - enableTools, - onToggleTools, - wsConnected, -}: ChatHeaderProps) { - const hasModelOptions = availableModels.length > 0 || claudeModels.length > 0; - const [showConfirm, setShowConfirm] = useState(false); - const [rebuildStatus, setRebuildStatus] = useState("idle"); - const [rebuildError, setRebuildError] = useState(null); - - // When WS reconnects after a rebuild, clear the reconnecting status. - useEffect(() => { - if (rebuildStatus === "reconnecting" && wsConnected) { - setRebuildStatus("idle"); - } - }, [wsConnected, rebuildStatus]); - - function handleRebuildClick() { - setRebuildError(null); - setShowConfirm(true); - } - - function handleRebuildConfirm() { - setShowConfirm(false); - setRebuildStatus("building"); - api - .rebuildAndRestart() - .then((result) => { - // Got a response = build failed (server still running). - setRebuildStatus("error"); - setRebuildError(result || "Rebuild failed"); - }) - .catch(() => { - // Network error = server is restarting (build succeeded). - setRebuildStatus("reconnecting"); - }); - } - - function handleRebuildCancel() { - setShowConfirm(false); - } - - function handleDismissError() { - setRebuildStatus("idle"); - setRebuildError(null); - } - - const rebuildButtonLabel = - rebuildStatus === "building" - ? "Building..." - : rebuildStatus === "reconnecting" - ? "Reconnecting..." - : rebuildStatus === "error" - ? "⚠ Rebuild Failed" - : "↺ Rebuild"; - - const rebuildButtonDisabled = - rebuildStatus === "building" || rebuildStatus === "reconnecting"; - - return ( - <> - {/* Confirmation dialog overlay */} - {showConfirm && ( -
-
-
- Rebuild and restart? -
-
- This will run cargo build and replace the running - server. All agents will be stopped. The page will reconnect - automatically when the new server is ready. -
-
- - -
-
-
- )} - - {/* Error toast */} - {rebuildStatus === "error" && rebuildError && ( -
-
-
-
- ⚠ Rebuild failed -
-
-								{rebuildError}
-							
-
- -
-
- )} - -
-
- - Storkit - -
- {projectPath} -
- -
- -
-
- {formatBuildTime(__BUILD_TIME__)} -
- -
- {getContextEmoji(contextUsage.percentage)} {contextUsage.percentage} - % -
- - - - - - {hasModelOptions ? ( - - ) : ( - onModelChange(e.target.value)} - placeholder="Model" - style={{ - padding: "6px 12px", - borderRadius: "99px", - border: "none", - fontSize: "0.9em", - background: "#2f2f2f", - color: "#ececec", - outline: "none", - }} - /> - )} - - -
-
- - ); -} diff --git a/frontend/src/components/ChatInput.test.tsx b/frontend/src/components/ChatInput.test.tsx deleted file mode 100644 index 02041db..0000000 --- a/frontend/src/components/ChatInput.test.tsx +++ /dev/null @@ -1,279 +0,0 @@ -import { act, fireEvent, render, screen } from "@testing-library/react"; -import * as React from "react"; -import { describe, expect, it, vi } from "vitest"; -import type { ChatInputHandle } from "./ChatInput"; -import { ChatInput } from "./ChatInput"; - -describe("ChatInput component (Story 178 AC1)", () => { - it("renders a textarea with Send a message... placeholder", () => { - render( - , - ); - - const textarea = screen.getByPlaceholderText("Send a message..."); - expect(textarea.tagName.toLowerCase()).toBe("textarea"); - }); - - it("manages input state internally — typing updates value without calling onSubmit", async () => { - const onSubmit = vi.fn(); - - render( - , - ); - - const textarea = screen.getByPlaceholderText("Send a message..."); - - await act(async () => { - fireEvent.change(textarea, { target: { value: "hello world" } }); - }); - - expect((textarea as HTMLTextAreaElement).value).toBe("hello world"); - expect(onSubmit).not.toHaveBeenCalled(); - }); - - it("calls onSubmit with the input text on Enter key press", async () => { - const onSubmit = vi.fn(); - - render( - , - ); - - const textarea = screen.getByPlaceholderText("Send a message..."); - - await act(async () => { - fireEvent.change(textarea, { target: { value: "test message" } }); - }); - await act(async () => { - fireEvent.keyDown(textarea, { key: "Enter", shiftKey: false }); - }); - - expect(onSubmit).toHaveBeenCalledWith("test message"); - }); - - it("clears input after submitting", async () => { - render( - , - ); - - const textarea = screen.getByPlaceholderText("Send a message..."); - - await act(async () => { - fireEvent.change(textarea, { target: { value: "hello" } }); - }); - await act(async () => { - fireEvent.keyDown(textarea, { key: "Enter", shiftKey: false }); - }); - - expect((textarea as HTMLTextAreaElement).value).toBe(""); - }); - - it("does not submit on Shift+Enter", async () => { - const onSubmit = vi.fn(); - - render( - , - ); - - const textarea = screen.getByPlaceholderText("Send a message..."); - - await act(async () => { - fireEvent.change(textarea, { target: { value: "multiline" } }); - }); - await act(async () => { - fireEvent.keyDown(textarea, { key: "Enter", shiftKey: true }); - }); - - expect(onSubmit).not.toHaveBeenCalled(); - expect((textarea as HTMLTextAreaElement).value).toBe("multiline"); - }); - - it("calls onCancel when stop button is clicked while loading with empty input", async () => { - const onCancel = vi.fn(); - - render( - , - ); - - const stopButton = screen.getByRole("button", { name: "■" }); - await act(async () => { - fireEvent.click(stopButton); - }); - - expect(onCancel).toHaveBeenCalled(); - }); - - it("renders queued message indicators", () => { - render( - , - ); - - const indicators = screen.getAllByTestId("queued-message-indicator"); - expect(indicators).toHaveLength(2); - expect(indicators[0]).toHaveTextContent("first message"); - expect(indicators[1]).toHaveTextContent("second message"); - }); - - it("calls onRemoveQueuedMessage when cancel button is clicked", async () => { - const onRemove = vi.fn(); - - render( - , - ); - - const cancelBtn = screen.getByTitle("Cancel queued message"); - await act(async () => { - fireEvent.click(cancelBtn); - }); - - expect(onRemove).toHaveBeenCalledWith("q1"); - }); - - it("edit button restores queued message text to input and removes from queue", async () => { - const onRemove = vi.fn(); - - render( - , - ); - - const editBtn = screen.getByTitle("Edit queued message"); - await act(async () => { - fireEvent.click(editBtn); - }); - - const textarea = screen.getByPlaceholderText("Send a message..."); - expect((textarea as HTMLTextAreaElement).value).toBe("edit me back"); - expect(onRemove).toHaveBeenCalledWith("q1"); - }); -}); - -describe("ChatInput appendToInput (Bug 215 regression)", () => { - it("appendToInput sets text into an empty input", async () => { - const ref = React.createRef(); - - render( - , - ); - - await act(async () => { - ref.current?.appendToInput("queued message"); - }); - - const textarea = screen.getByPlaceholderText("Send a message..."); - expect((textarea as HTMLTextAreaElement).value).toBe("queued message"); - }); - - it("appendToInput appends to existing input content with a newline separator", async () => { - const ref = React.createRef(); - - render( - , - ); - - const textarea = screen.getByPlaceholderText("Send a message..."); - - await act(async () => { - fireEvent.change(textarea, { target: { value: "existing text" } }); - }); - - await act(async () => { - ref.current?.appendToInput("appended text"); - }); - - expect((textarea as HTMLTextAreaElement).value).toBe( - "existing text\nappended text", - ); - }); - - it("multiple queued messages joined with newlines are appended on cancel", async () => { - const ref = React.createRef(); - - render( - , - ); - - await act(async () => { - ref.current?.appendToInput("msg one\nmsg two"); - }); - - const textarea = screen.getByPlaceholderText("Send a message..."); - expect((textarea as HTMLTextAreaElement).value).toBe("msg one\nmsg two"); - }); -}); diff --git a/frontend/src/components/ChatInput.tsx b/frontend/src/components/ChatInput.tsx deleted file mode 100644 index c560881..0000000 --- a/frontend/src/components/ChatInput.tsx +++ /dev/null @@ -1,419 +0,0 @@ -import * as React from "react"; -import { api } from "../api/client"; - -const { - forwardRef, - useCallback, - useEffect, - useImperativeHandle, - useRef, - useState, -} = React; - -export interface ChatInputHandle { - appendToInput(text: string): void; -} - -interface ChatInputProps { - loading: boolean; - queuedMessages: { id: string; text: string }[]; - onSubmit: (message: string) => void; - onCancel: () => void; - onRemoveQueuedMessage: (id: string) => void; -} - -/** Fuzzy-match: returns true if all chars of `query` appear in order in `str`. */ -function fuzzyMatch(str: string, query: string): boolean { - if (!query) return true; - const lower = str.toLowerCase(); - const q = query.toLowerCase(); - let qi = 0; - for (let i = 0; i < lower.length && qi < q.length; i++) { - if (lower[i] === q[qi]) qi++; - } - return qi === q.length; -} - -/** Score a fuzzy match: lower is better. Exact prefix match wins, then shorter paths. */ -function fuzzyScore(str: string, query: string): number { - const lower = str.toLowerCase(); - const q = query.toLowerCase(); - // Prefer matches where query appears as a contiguous substring - if (lower.includes(q)) return lower.indexOf(q); - return str.length; -} - -interface FilePickerOverlayProps { - query: string; - files: string[]; - selectedIndex: number; - onSelect: (file: string) => void; - onDismiss: () => void; - anchorRef: React.RefObject; -} - -function FilePickerOverlay({ - query, - files, - selectedIndex, - onSelect, -}: FilePickerOverlayProps) { - const filtered = files - .filter((f) => fuzzyMatch(f, query)) - .sort((a, b) => fuzzyScore(a, query) - fuzzyScore(b, query)) - .slice(0, 10); - - if (filtered.length === 0) return null; - - return ( -
- {filtered.map((file, idx) => ( - - ))} -
- ); -} - -export const ChatInput = forwardRef( - function ChatInput( - { loading, queuedMessages, onSubmit, onCancel, onRemoveQueuedMessage }, - ref, - ) { - const [input, setInput] = useState(""); - const inputRef = useRef(null); - - // File picker state - const [projectFiles, setProjectFiles] = useState([]); - const [pickerQuery, setPickerQuery] = useState(null); - const [pickerSelectedIndex, setPickerSelectedIndex] = useState(0); - const [pickerAtStart, setPickerAtStart] = useState(0); - - useImperativeHandle(ref, () => ({ - appendToInput(text: string) { - setInput((prev) => (prev ? `${prev}\n${text}` : text)); - }, - })); - - useEffect(() => { - inputRef.current?.focus(); - }, []); - - // Compute filtered files for current picker query - const filteredFiles = - pickerQuery !== null - ? projectFiles - .filter((f) => fuzzyMatch(f, pickerQuery)) - .sort( - (a, b) => fuzzyScore(a, pickerQuery) - fuzzyScore(b, pickerQuery), - ) - .slice(0, 10) - : []; - - const dismissPicker = useCallback(() => { - setPickerQuery(null); - setPickerSelectedIndex(0); - }, []); - - const selectFile = useCallback( - (file: string) => { - // Replace the @query portion with @file - const before = input.slice(0, pickerAtStart); - const cursorPos = inputRef.current?.selectionStart ?? input.length; - const after = input.slice(cursorPos); - setInput(`${before}@${file}${after}`); - dismissPicker(); - // Restore focus after state update - setTimeout(() => inputRef.current?.focus(), 0); - }, - [input, pickerAtStart, dismissPicker], - ); - - const handleInputChange = useCallback( - (e: React.ChangeEvent) => { - const val = e.target.value; - setInput(val); - - const cursor = e.target.selectionStart ?? val.length; - // Find the last @ before the cursor that starts a reference token - const textUpToCursor = val.slice(0, cursor); - // Match @ not preceded by non-whitespace (i.e. @ at start or after space/newline) - const atMatch = textUpToCursor.match(/(^|[\s\n])@([^\s@]*)$/); - - if (atMatch) { - const query = atMatch[2]; - const atPos = textUpToCursor.lastIndexOf("@"); - setPickerAtStart(atPos); - setPickerQuery(query); - setPickerSelectedIndex(0); - - // Lazily load files on first trigger - if (projectFiles.length === 0) { - api - .listProjectFiles() - .then(setProjectFiles) - .catch(() => {}); - } - } else { - if (pickerQuery !== null) dismissPicker(); - } - }, - [projectFiles.length, pickerQuery, dismissPicker], - ); - - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (pickerQuery !== null && filteredFiles.length > 0) { - if (e.key === "ArrowDown") { - e.preventDefault(); - setPickerSelectedIndex((i) => - Math.min(i + 1, filteredFiles.length - 1), - ); - return; - } - if (e.key === "ArrowUp") { - e.preventDefault(); - setPickerSelectedIndex((i) => Math.max(i - 1, 0)); - return; - } - if (e.key === "Enter" || e.key === "Tab") { - e.preventDefault(); - selectFile(filteredFiles[pickerSelectedIndex] ?? filteredFiles[0]); - return; - } - if (e.key === "Escape") { - e.preventDefault(); - dismissPicker(); - return; - } - } else if (e.key === "Escape" && pickerQuery !== null) { - e.preventDefault(); - dismissPicker(); - return; - } - - if (e.key === "Enter" && !e.shiftKey) { - e.preventDefault(); - handleSubmit(); - } - }, - [ - pickerQuery, - filteredFiles, - pickerSelectedIndex, - selectFile, - dismissPicker, - ], - ); - - const handleSubmit = () => { - if (!input.trim()) return; - onSubmit(input); - setInput(""); - dismissPicker(); - }; - - return ( -
-
- {/* Queued message indicators */} - {queuedMessages.map(({ id, text }) => ( -
- - Queued - - - {text} - - - -
- ))} - {/* Input row with file picker overlay */} -
- {pickerQuery !== null && ( - - )} -