diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index f94d980eb..3cd29c149 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -10,10 +10,10 @@ jobs: build: name: Build Linux x86_64 runs-on: ubuntu-latest - + steps: - uses: actions/checkout@v4 - + - name: Install system dependencies run: | sudo apt-get update @@ -24,36 +24,36 @@ jobs: libssl-dev \ libayatana-appindicator3-dev \ librsvg2-dev - + - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: targets: x86_64-unknown-linux-gnu - + - name: Setup Rust cache uses: Swatinem/rust-cache@v2 with: workspaces: src-tauri - + - name: Setup Bun uses: oven-sh/setup-bun@v2 - + - name: Install dependencies run: bun install - + - name: Build Tauri app run: bun run tauri build --target x86_64-unknown-linux-gnu - + - name: Create artifacts directory run: | mkdir -p dist/linux-x86_64 cp src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb/*.deb dist/linux-x86_64/ || true cp src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/appimage/*.AppImage dist/linux-x86_64/ || true - + # Generate checksums cd dist/linux-x86_64 sha256sum * > checksums.txt - + - name: Upload artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 9a65d94e1..f8f7a5316 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -3,9 +3,9 @@ name: Build Test # Trigger on every push and pull request on: push: - branches: [ main, develop, 'release/**', 'feature/**' ] + branches: [main, develop, "release/**", "feature/**"] pull_request: - branches: [ main, develop ] + branches: [main, develop] types: [opened, synchronize, reopened] # Cancel in-progress workflows when a new commit is pushed @@ -20,7 +20,7 @@ env: jobs: build-test: name: Build Test (${{ matrix.platform.name }}) - + strategy: fail-fast: false matrix: @@ -37,9 +37,9 @@ jobs: - name: macOS os: macos-latest rust-target: x86_64-apple-darwin - + runs-on: ${{ matrix.platform.os }} - + steps: # Checkout the repository - name: Checkout repository @@ -75,9 +75,9 @@ jobs: - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 with: - workspaces: './src-tauri -> target' + workspaces: "./src-tauri -> target" key: ${{ matrix.platform.os }}-rust-${{ hashFiles('**/Cargo.lock') }} - + # Setup Bun - name: Setup Bun uses: oven-sh/setup-bun@v2 @@ -94,7 +94,7 @@ jobs: key: ${{ matrix.platform.os }}-bun-${{ hashFiles('bun.lockb', 'package.json') }} restore-keys: | ${{ matrix.platform.os }}-bun- - + # Install frontend dependencies - name: Install frontend dependencies run: bun install --frozen-lockfile @@ -127,7 +127,7 @@ jobs: runs-on: ubuntu-latest needs: [build-test] if: always() - + steps: - name: Check build results run: | @@ -149,21 +149,21 @@ jobs: const result = '${{ needs.build-test.result }}'; const emoji = result === 'success' ? '✅' : '❌'; const status = result === 'success' ? 'All build tests passed!' : 'Build tests failed'; - + // Create a comment summarizing the build status const comment = `## ${emoji} Build Test Results - + **Status**: ${status} **Commit**: ${{ github.event.pull_request.head.sha || github.sha }} - + | Platform | Status | |----------|--------| | Linux | ${{ contains(needs.build-test.result, 'success') && '✅' || '❌' }} | | Windows | ${{ contains(needs.build-test.result, 'success') && '✅' || '❌' }} | | macOS | ${{ contains(needs.build-test.result, 'success') && '✅' || '❌' }} | - + [View full workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})`; - + // Only post comment if it's a PR if (context.eventName === 'pull_request') { await github.rest.issues.createComment({ @@ -172,4 +172,4 @@ jobs: issue_number: context.issue.number, body: comment }); - } + } diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index ecd27d0a5..e9ebc818a 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -17,14 +17,14 @@ jobs: # github.event.pull_request.user.login == 'external-contributor' || # github.event.pull_request.user.login == 'new-developer' || # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' - + runs-on: ubuntu-latest permissions: contents: read pull-requests: read issues: read id-token: write - + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -36,10 +36,10 @@ jobs: uses: anthropics/claude-code-action@beta with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - + # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4) # model: "claude-opus-4-20250514" - + # Direct prompt for automated review (no @claude mention needed) direct_prompt: | Please review this pull request and provide feedback on: @@ -48,9 +48,9 @@ jobs: - Performance considerations - Security concerns - Test coverage - + Be constructive and helpful in your feedback. - + # Optional: Customize review based on file types # direct_prompt: | # Review this PR focusing on: @@ -58,18 +58,17 @@ jobs: # - For API endpoints: Security, input validation, and error handling # - For React components: Performance, accessibility, and best practices # - For tests: Coverage, edge cases, and test quality - + # Optional: Different prompts for different authors # direct_prompt: | - # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && + # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' || # 'Please provide a thorough code review focusing on our coding standards and best practices.' }} - + # Optional: Add specific tools for running tests or linting # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)" - + # Optional: Skip review for certain conditions # if: | # !contains(github.event.pull_request.title, '[skip-review]') && # !contains(github.event.pull_request.title, '[WIP]') - diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 40088f80f..692b45a45 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -34,26 +34,25 @@ jobs: uses: anthropics/claude-code-action@beta with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - + # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4) model: "claude-opus-4-20250514" - + # Optional: Customize the trigger phrase (default: @claude) # trigger_phrase: "/claude" - + # Optional: Trigger when specific user is assigned to an issue # assignee_trigger: "claude-bot" - + # Optional: Allow Claude to run specific commands # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)" - + # Optional: Add custom instructions for Claude to customize its behavior for your project # custom_instructions: | # Follow our coding standards # Ensure all new code has tests # Use TypeScript for new files - + # Optional: Custom environment variables for Claude # claude_env: | # NODE_ENV: test - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 278c13aee..7c7ef5543 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,11 +3,11 @@ name: Release on: push: tags: - - 'v*' + - "v*" workflow_dispatch: inputs: version: - description: 'Version to release (e.g., v1.0.0)' + description: "Version to release (e.g., v1.0.0)" required: true type: string @@ -19,21 +19,20 @@ jobs: build-linux: uses: ./.github/workflows/build-linux.yml secrets: inherit - + build-macos: uses: ./.github/workflows/build-macos.yml secrets: inherit - # Create release after all builds complete create-release: name: Create Release needs: [build-linux, build-macos] runs-on: ubuntu-latest - + steps: - uses: actions/checkout@v4 - + - name: Determine version id: version run: | @@ -44,36 +43,36 @@ jobs: fi echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Version: $VERSION" - + - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts - + - name: Prepare release assets run: | mkdir -p release-assets - + # Linux artifacts if [ -d "artifacts/linux-x86_64" ]; then cp artifacts/linux-x86_64/*.deb release-assets/opcode_${{ steps.version.outputs.version }}_linux_x86_64.deb || true cp artifacts/linux-x86_64/*.AppImage release-assets/opcode_${{ steps.version.outputs.version }}_linux_x86_64.AppImage || true fi - + # macOS artifacts if [ -d "artifacts/macos-universal" ]; then cp artifacts/macos-universal/opcode.dmg release-assets/opcode_${{ steps.version.outputs.version }}_macos_universal.dmg || true cp artifacts/macos-universal/opcode.app.zip release-assets/opcode_${{ steps.version.outputs.version }}_macos_universal.app.tar.gz || true fi - + # Create source code archives # Clean version without 'v' prefix for archive names CLEAN_VERSION="${{ steps.version.outputs.version }}" CLEAN_VERSION="${CLEAN_VERSION#v}" - + # Create source code archives (excluding .git and other unnecessary files) echo "Creating source code archives..." - + # Create a clean export of the repository git archive --format=tar.gz --prefix=opcode-${CLEAN_VERSION}/ -o release-assets/opcode-${CLEAN_VERSION}.tar.gz HEAD git archive --format=zip --prefix=opcode-${CLEAN_VERSION}/ -o release-assets/opcode-${CLEAN_VERSION}.zip HEAD @@ -86,7 +85,7 @@ jobs: fi done cd .. - + - name: Create Release uses: softprops/action-gh-release@v1 with: @@ -117,4 +116,3 @@ jobs: - macOS: Open the `.dmg` and drag opcode to Applications. - Linux: `chmod +x` the `.AppImage` and run it, or install the `.deb` on Debian/Ubuntu. - diff --git a/CLAUDIA-SERVER.md b/CLAUDIA-SERVER.md new file mode 100644 index 000000000..85a79327f --- /dev/null +++ b/CLAUDIA-SERVER.md @@ -0,0 +1,151 @@ +# Claudia Server - TypeScript Implementation + +This directory contains a complete standalone TypeScript server implementation of Claudia's Claude Code integration functionality. + +## What is Claudia Server? + +Claudia Server extracts the core Claude Code CLI wrapper functionality from the original Claudia desktop application and provides it as a standalone TypeScript/Node.js HTTP and WebSocket server. This allows you to integrate Claude Code into any application using standard web protocols. + +## Key Benefits + +- **Language Agnostic**: Use any programming language that can make HTTP requests +- **Standalone**: No desktop app required, runs as a service +- **Real-time**: WebSocket streaming for live Claude responses +- **Lightweight**: Pure TypeScript/Node.js implementation +- **API-First**: RESTful API design with comprehensive documentation +- **Cross-platform**: Works on Windows, macOS, and Linux + +## Quick Start + +```bash +cd claudia-server +npm install +npm run build +npm start +``` + +The server will start on `http://localhost:3000` with WebSocket support at `ws://localhost:3000/ws`. + +## Documentation + +- **[README.md](README.md)** - Complete API documentation +- **[QUICKSTART.md](QUICKSTART.md)** - Quick start guide +- **[examples/](examples/)** - Client examples in multiple languages + +## Architecture + +The server implements the same functionality as the Rust/Tauri backend: + +``` +┌─────────────────┐ HTTP/WS ┌─────────────────┐ CLI ┌─────────────────┐ +│ Your Client │ ────────────► │ Claudia Server │ ─────────► │ Claude Code │ +│ (Any Language) │ │ (TypeScript) │ │ CLI │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +## Features Implemented + +✅ Claude Code CLI integration with auto-discovery +✅ Real-time streaming via WebSocket +✅ Project and session management +✅ CLAUDE.md file operations +✅ Process management and monitoring +✅ Health checks and server status +✅ Comprehensive error handling +✅ CORS and security middleware +✅ Complete API documentation +✅ Multiple client examples + +## API Endpoints + +| Endpoint | Method | Description | +|----------------------------------|-----------|--------------------------------| +| `/api/status/health` | GET | Server health check | +| `/api/status/info` | GET | Server info/config summary | +| `/api/claude/version` | GET | Claude CLI version | +| `/api/claude/execute` | POST | Start new Claude session | +| `/api/claude/continue` | POST | Continue an existing session | +| `/api/claude/resume` | POST | Resume a paused session | +| `/api/claude/cancel/{id}` | POST | Cancel a running session | +| `/api/claude/sessions/running` | GET | List running sessions | +| `/api/projects` | GET | List all projects | +| `/api/projects` | POST | Create a new project | +| `/ws` | WebSocket | Real-time streaming | + +See [README.md](README.md) for complete API documentation. + +## Client Examples + +### JavaScript +```javascript +const response = await fetch('http://localhost:3000/api/claude/execute', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + project_path: '/path/to/project', + prompt: 'Help me code', + model: 'claude-3-5-sonnet-20241022' + }) +}); +``` + +### Python +```python +import aiohttp + +async with aiohttp.ClientSession() as session: + async with session.post('http://localhost:3000/api/claude/execute', + json={'project_path': '/path', 'prompt': 'Help me'}) as resp: + result = await resp.json() +``` + +### curl +```bash +curl -X POST http://localhost:3000/api/claude/execute \ + -H "Content-Type: application/json" \ + -d '{"project_path": "/path", "prompt": "Help me", "model": "claude-3-5-sonnet-20241022"}' +``` + +## Use Cases + +- **Web Applications**: Integrate Claude Code into web apps +- **Mobile Apps**: Use from iOS/Android via HTTP API +- **CI/CD Pipelines**: Automated code analysis and generation +- **IDEs/Editors**: Custom editor integrations +- **Microservices**: Claude Code as a microservice +- **Chatbots**: Build Claude-powered chatbots +- **API Gateways**: Proxy Claude functionality + +## Deployment + +The server can be deployed anywhere Node.js runs: + +- **Local Development**: `npm start` +- **Production**: PM2, Docker, Kubernetes +- **Cloud**: AWS, GCP, Azure, Heroku +- **Edge**: Vercel, Netlify Functions + +## Comparison with Desktop App + +| Feature | Desktop App | TypeScript Server | +|---------|-------------|-------------------| +| **Interface** | GUI (Tauri/React) | HTTP/WebSocket API | +| **Language** | Rust + TypeScript | Pure TypeScript | +| **Deployment** | Desktop installation | Web service | +| **Integration** | Standalone app | API for any client | +| **Scalability** | Single user | Multi-user capable | +| **Platform** | Desktop platforms | Any HTTP client | + +## Getting Started + +1. **Install dependencies**: `npm install` +2. **Build the project**: `npm run build` +3. **Start the server**: `npm start` +4. **Test the API**: `curl http://localhost:3000/api/status/health` +5. **Try examples**: See `examples/` directory + +For detailed setup and usage instructions, see [QUICKSTART.md](QUICKSTART.md). + +--- + +This TypeScript server provides the same powerful Claude Code integration as the desktop app, but as a flexible web service that can be integrated into any application or workflow. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f68a80cd4..820961266 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,7 @@ To contribute, please follow these steps: When submitting a pull request, please follow these guidelines: 1. **Title**: Please include following prefixes: + - `Feature:` for new features - `Fix:` for bug fixes - `Docs:` for documentation changes @@ -24,6 +25,7 @@ When submitting a pull request, please follow these guidelines: - `Other:` for other changes For example: + - `Feature: added custom agent timeout configuration` - `Fix: resolved session list scrolling issue` @@ -40,12 +42,14 @@ When submitting a pull request, please follow these guidelines: ## Coding Standards ### Frontend (React/TypeScript) + - Use TypeScript for all new code - Follow functional components with hooks - Use Tailwind CSS for styling - Add JSDoc comments for exported functions and components ### Backend (Rust) + - Follow Rust standard conventions - Use `cargo fmt` for formatting - Use `cargo clippy` for linting @@ -53,15 +57,17 @@ When submitting a pull request, please follow these guidelines: - Add comprehensive documentation with `///` comments ### Security Requirements + - Validate all inputs from the frontend - Use prepared statements for database operations - Never log sensitive data (tokens, passwords, etc.) - Use secure defaults for all configurations ## Testing + - Add tests for new functionality - Ensure all existing tests pass - Run `cargo test` for Rust code - Test the application manually before submitting -Please adhere to the coding conventions, maintain clear documentation, and provide thorough testing for your contributions. +Please adhere to the coding conventions, maintain clear documentation, and provide thorough testing for your contributions. diff --git a/bun.lock b/bun.lock index 0152c93a4..21d229052 100644 --- a/bun.lock +++ b/bun.lock @@ -32,6 +32,7 @@ "date-fns": "^3.6.0", "diff": "^8.0.2", "framer-motion": "^12.0.0-alpha.1", + "hast-util-sanitize": "^5.0.2", "html2canvas": "^1.4.1", "lucide-react": "^0.468.0", "posthog-js": "^1.258.3", @@ -41,6 +42,8 @@ "react-markdown": "^9.0.3", "react-syntax-highlighter": "^15.6.1", "recharts": "^2.14.1", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.0", "tailwind-merge": "^2.6.0", "tailwindcss": "^4.1.8", @@ -659,6 +662,8 @@ "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], + "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="], + "hast-util-select": ["hast-util-select@6.0.4", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "bcp-47-match": "^2.0.0", "comma-separated-tokens": "^2.0.0", "css-selector-parser": "^3.0.0", "devlop": "^1.0.0", "direction": "^2.0.0", "hast-util-has-property": "^3.0.0", "hast-util-to-string": "^3.0.0", "hast-util-whitespace": "^3.0.0", "nth-check": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw=="], "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], @@ -931,6 +936,8 @@ "rehype-rewrite": ["rehype-rewrite@4.0.2", "", { "dependencies": { "hast-util-select": "^6.0.0", "unified": "^11.0.3", "unist-util-visit": "^5.0.0" } }, "sha512-rjLJ3z6fIV11phwCqHp/KRo8xuUCO8o9bFJCNw5o6O2wlLk6g8r323aRswdGBQwfXPFYeSuZdAjp4tzo6RGqEg=="], + "rehype-sanitize": ["rehype-sanitize@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-sanitize": "^5.0.0" } }, "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg=="], + "rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="], "rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="], diff --git a/claudia-server/.eslintrc.js b/claudia-server/.eslintrc.js new file mode 100644 index 000000000..cf214079a --- /dev/null +++ b/claudia-server/.eslintrc.js @@ -0,0 +1,24 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + extends: [ + 'eslint:recommended', + '@typescript-eslint/recommended', + ], + rules: { + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'warn', + }, + env: { + node: true, + es6: true, + jest: true, + }, +}; \ No newline at end of file diff --git a/claudia-server/.gitignore b/claudia-server/.gitignore new file mode 100644 index 000000000..45e240da8 --- /dev/null +++ b/claudia-server/.gitignore @@ -0,0 +1,20 @@ +node_modules/ +dist/ +.env +.env.local +.env.*.local +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.DS_Store +.vscode/ +.idea/ +*.swp +*.swo +*~ +coverage/ +.nyc_output/ +.tmp/ +.cache/ \ No newline at end of file diff --git a/claudia-server/.prettierrc b/claudia-server/.prettierrc new file mode 100644 index 000000000..a813c658a --- /dev/null +++ b/claudia-server/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "useTabs": false +} \ No newline at end of file diff --git a/claudia-server/QUICKSTART.md b/claudia-server/QUICKSTART.md new file mode 100644 index 000000000..a28c5ec4c --- /dev/null +++ b/claudia-server/QUICKSTART.md @@ -0,0 +1,202 @@ +# Claudia Server - Quick Start Guide + +## Overview + +Claudia Server is a standalone TypeScript/Node.js server that provides HTTP REST API and WebSocket streaming access to Claude Code functionality. It extracts the core Claude Code integration from the Claudia desktop app and makes it available as a web service. + +## Installation & Setup + +### Prerequisites +- Node.js 18.0.0 or later +- Claude Code CLI installed and available in PATH +- npm or similar package manager + +### Quick Installation +```bash +cd claudia-server +npm install +npm run build +``` + +### Start the Server +```bash +# Start with default settings (port 3000) +npm start + +# Or start with custom port +node dist/index.js --port 8080 + +# Show all options +node dist/index.js --help +``` + +## API Usage + +### Health Check +```bash +curl http://localhost:3000/api/status/health +``` + +### Start Claude Session +```bash +curl -X POST http://localhost:3000/api/claude/execute \ + -H "Content-Type: application/json" \ + -d '{ + "project_path": "/path/to/your/project", + "prompt": "Help me understand this codebase", + "model": "claude-3-5-sonnet-20241022" + }' +``` + +### WebSocket Streaming +```javascript +const ws = new WebSocket('ws://localhost:3000/ws'); + +ws.onopen = () => { + // Subscribe to session updates + ws.send(JSON.stringify({ + type: 'subscribe', + session_id: 'your-session-id' + })); +}; + +ws.onmessage = (event) => { + const message = JSON.parse(event.data); + console.log('Claude response:', message); +}; +``` + +## Key Features + +- 🚀 **HTTP REST API** - Standard REST endpoints for all Claude operations +- 📡 **WebSocket Streaming** - Real-time streaming of Claude responses +- 🗂️ **Project Management** - Full project and session management +- 📝 **CLAUDE.md Support** - Read/write CLAUDE.md files +- 🔄 **Session Control** - Start, continue, resume, and cancel Claude sessions +- 🏠 **Auto-Discovery** - Automatically finds Claude binary installation + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/status/health` | Health check | +| GET | `/api/status/info` | Server information | +| GET | `/api/claude/version` | Claude version info | +| POST | `/api/claude/execute` | Start new Claude session | +| POST | `/api/claude/continue` | Continue conversation | +| POST | `/api/claude/resume` | Resume existing session | +| POST | `/api/claude/cancel/{id}` | Cancel running session | +| GET | `/api/claude/sessions/running` | List running sessions | +| GET | `/api/projects` | List all projects | +| POST | `/api/projects` | Create new project | +| WS | `/ws` | WebSocket endpoint | + +## Client Examples + +- **JavaScript/Node.js**: `examples/javascript/client.js` +- **Python**: `examples/python/client.py` +- **curl**: `examples/curl/api-examples.sh` + +## Configuration + +### Environment Variables +- `PORT` - Server port (default: 3000) +- `HOST` - Server host (default: 0.0.0.0) +- `CLAUDE_BINARY` - Path to Claude binary +- `CLAUDE_HOME` - Claude home directory + +### Command Line Options +```bash +node dist/index.js --port 3000 --host localhost --claude-binary /usr/bin/claude +``` + +## Development + +### Watch Mode +```bash +npm run dev +``` + +### Build +```bash +npm run build +``` + +### Lint & Format +```bash +npm run lint +npm run format +``` + +## Troubleshooting + +### Claude Binary Not Found +``` +Error: Claude binary not found. Please install Claude Code CLI. +``` +**Solution**: Install Claude Code CLI or specify path with `--claude-binary` + +### Port Already in Use +``` +Error: listen EADDRINUSE: address already in use :::3000 +``` +**Solution**: Use different port with `--port 3001` + +### WebSocket Connection Issues +**Solution**: Check firewall settings and ensure server is accessible + +## Production Deployment + +### Using PM2 +```bash +npm install -g pm2 +pm2 start dist/index.js --name claudia-server -- --port 3000 +pm2 save +pm2 startup +``` + +### Using Docker +```dockerfile +FROM node:18-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm install --production +COPY dist ./dist +EXPOSE 3000 +CMD ["node", "dist/index.js"] +``` + +### Nginx Proxy +```nginx +server { + listen 80; + server_name your-domain.com; + + location / { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location /ws { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + } +} +``` + +## Support + +For detailed documentation, see `README.md` +For examples, see `examples/` directory +For issues, visit the [GitHub repository](https://github.com/getAsterisk/claudia) + +--- + +This standalone TypeScript server provides the same core Claude Code functionality as the original Claudia desktop app, but as a web service that can be integrated with any client application. \ No newline at end of file diff --git a/claudia-server/README.md b/claudia-server/README.md new file mode 100644 index 000000000..f4e39ad9c --- /dev/null +++ b/claudia-server/README.md @@ -0,0 +1,679 @@ +# Claudia Server + +A standalone TypeScript server for Claude Code integration that provides HTTP REST API and WebSocket streaming capabilities. + +## Table of Contents + +- [Overview](#overview) +- [Features](#features) +- [Installation](#installation) +- [Quick Start](#quick-start) +- [API Reference](#api-reference) +- [WebSocket API](#websocket-api) +- [Configuration](#configuration) +- [Examples](#examples) +- [Troubleshooting](#troubleshooting) + +## Overview + +Claudia Server extracts the core Claude Code integration functionality from the original Claudia desktop application and provides it as a standalone TypeScript/Node.js server. This allows you to integrate Claude Code into any application using standard HTTP and WebSocket protocols. + +The server wraps the Claude Code CLI and provides: +- RESTful API for Claude Code operations +- Real-time streaming via WebSocket +- Project and session management +- CLAUDE.md file management +- Process monitoring and control + +## Features + +- 🚀 **HTTP REST API** - Standard REST endpoints for all Claude operations +- 📡 **WebSocket Streaming** - Real-time streaming of Claude responses +- 🗂️ **Project Management** - Full project and session management +- 📝 **CLAUDE.md Support** - Read/write CLAUDE.md files +- 🔄 **Session Control** - Start, continue, resume, and cancel Claude sessions +- 🏠 **Auto-Discovery** - Automatically finds Claude binary installation +- 🔐 **Process Management** - Track and manage running Claude processes +- 📊 **Health Monitoring** - Health checks and server status endpoints + +## Installation + +### Prerequisites + +- Node.js 18.0.0 or later +- Claude Code CLI installed and available in PATH +- TypeScript (for development) + +### Install Dependencies + +```bash +cd claudia-server +npm install +``` + +### Build + +```bash +npm run build +``` + +## Quick Start + +### Start the Server + +```bash +# Development mode with hot reload +npm run dev + +# Production mode +npm run build && npm start + +# Or using the CLI +./dist/index.js --port 3000 --host localhost +``` + +### Verify Installation + +```bash +# Check server health +curl http://localhost:3000/api/status/health + +# Check Claude version +curl http://localhost:3000/api/claude/version +``` + +## API Reference + +### Base URL + +All API endpoints are prefixed with `/api`: +- `http://localhost:3000/api` + +### Authentication + +Currently, no authentication is required. All endpoints are publicly accessible. + +### Response Format + +All API responses follow a consistent format: + +**Success Response:** +```json +{ + "success": true, + "data": { /* response data */ }, + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +**Error Response:** +```json +{ + "error": "Error message", + "code": "ERROR_CODE", + "timestamp": "2024-01-01T12:00:00.000Z", + "details": { /* optional error details */ } +} +``` + +### Status Endpoints + +#### Health Check +```http +GET /api/status/health +``` + +Returns server health status and basic metrics. + +**Response:** +```json +{ + "success": true, + "data": { + "status": "healthy", + "uptime": 3600, + "memory": { + "rss": 50331648, + "heapTotal": 20971520, + "heapUsed": 15728640 + }, + "version": "v18.17.0" + }, + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Server Info +```http +GET /api/status/info +``` + +Returns detailed server information. + +#### Home Directory +```http +GET /api/status/home +``` + +Returns user home directory and Claude directory paths. + +### Claude Endpoints + +#### Check Claude Version +```http +GET /api/claude/version +``` + +Check if Claude Code is installed and get version information. + +**Response:** +```json +{ + "success": true, + "data": { + "is_installed": true, + "version": "0.8.0", + "output": "claude 0.8.0" + }, + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Execute Claude Code +```http +POST /api/claude/execute +``` + +Start a new Claude Code session with the given prompt. + +**Request Body:** +```json +{ + "project_path": "/path/to/your/project", + "prompt": "Help me refactor this code", + "model": "claude-3-5-sonnet-20241022" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "session_id": "550e8400-e29b-41d4-a716-446655440000" + }, + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Continue Claude Conversation +```http +POST /api/claude/continue +``` + +Continue an existing Claude Code conversation in the current project. + +**Request Body:** +```json +{ + "project_path": "/path/to/your/project", + "prompt": "Can you also add error handling?", + "model": "claude-3-5-sonnet-20241022" +} +``` + +#### Resume Claude Session +```http +POST /api/claude/resume +``` + +Resume a specific Claude Code session by ID. + +**Request Body:** +```json +{ + "project_path": "/path/to/your/project", + "session_id": "550e8400-e29b-41d4-a716-446655440000", + "prompt": "Continue from where we left off", + "model": "claude-3-5-sonnet-20241022" +} +``` + +#### Cancel Claude Session +```http +POST /api/claude/cancel/{sessionId} +``` + +Cancel a running Claude Code session. + +**Response:** +```json +{ + "success": true, + "data": { + "cancelled": true, + "session_id": "550e8400-e29b-41d4-a716-446655440000" + }, + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### List Running Sessions +```http +GET /api/claude/sessions/running +``` + +Get list of currently running Claude sessions. + +**Response:** +```json +{ + "success": true, + "data": [ + { + "run_id": 1704110400000, + "process_type": { + "ClaudeSession": { + "session_id": "550e8400-e29b-41d4-a716-446655440000" + } + }, + "pid": 12345, + "started_at": "2024-01-01T12:00:00.000Z", + "project_path": "/path/to/project", + "task": "Help me refactor this code", + "model": "claude-3-5-sonnet-20241022" + } + ], + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Get Session Info +```http +GET /api/claude/sessions/{sessionId} +``` + +Get information about a specific session. + +#### Get Session History +```http +GET /api/claude/sessions/{sessionId}/history +``` + +Get the complete history/output for a session. + +### Project Endpoints + +#### List Projects +```http +GET /api/projects +``` + +List all Claude projects from `~/.claude/projects/`. + +**Response:** +```json +{ + "success": true, + "data": [ + { + "id": "-Users-username-dev-myproject", + "path": "/Users/username/dev/myproject", + "sessions": ["550e8400-e29b-41d4-a716-446655440000"], + "created_at": 1704110400, + "most_recent_session": 1704110400 + } + ], + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Create Project +```http +POST /api/projects +``` + +Create a new project for the given path. + +**Request Body:** +```json +{ + "path": "/path/to/your/project" +} +``` + +#### Get Project Sessions +```http +GET /api/projects/{projectId}/sessions +``` + +Get all sessions for a specific project. + +**Response:** +```json +{ + "success": true, + "data": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "project_id": "-Users-username-dev-myproject", + "project_path": "/Users/username/dev/myproject", + "created_at": 1704110400, + "first_message": "Help me refactor this code", + "message_timestamp": "2024-01-01T12:00:00.000Z" + } + ], + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Find CLAUDE.md Files +```http +GET /api/projects/{projectId}/claude-files +``` + +Find all CLAUDE.md files in a project. + +#### Read CLAUDE.md File +```http +GET /api/projects/claude-file?path=/path/to/CLAUDE.md +``` + +Read the content of a CLAUDE.md file. + +#### Save CLAUDE.md File +```http +PUT /api/projects/claude-file +``` + +Save content to a CLAUDE.md file. + +**Request Body:** +```json +{ + "path": "/path/to/CLAUDE.md", + "content": "# Project Instructions\\n\\nThis is my project..." +} +``` + +#### List Directory Contents +```http +GET /api/projects/directory?path=/path/to/directory +``` + +List contents of a directory. + +## WebSocket API + +### Connection + +Connect to the WebSocket endpoint: +``` +ws://localhost:3000/ws +``` + +### Message Format + +All WebSocket messages follow this format: + +```json +{ + "type": "message_type", + "data": { /* message data */ }, + "session_id": "optional_session_id", + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +### Client Messages + +#### Subscribe to Session +```json +{ + "type": "subscribe", + "session_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +#### Unsubscribe from Session +```json +{ + "type": "unsubscribe", + "session_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Server Messages + +#### Connection Status +```json +{ + "type": "status", + "data": { + "status": "connected", + "client_id": "client_1704110400000_abc123" + }, + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Claude Stream Messages +```json +{ + "type": "claude_stream", + "data": { + "type": "partial", + "content": "I'll help you refactor that code...", + "role": "assistant", + "timestamp": "2024-01-01T12:00:00.000Z" + }, + "session_id": "550e8400-e29b-41d4-a716-446655440000", + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +#### Error Messages +```json +{ + "type": "error", + "data": { + "error": "Error message", + "details": {} + }, + "timestamp": "2024-01-01T12:00:00.000Z" +} +``` + +## Configuration + +### Command Line Options + +```bash +claudia-server [options] + +Options: + -p, --port Server port (default: 3000) + -h, --host Server host (default: 0.0.0.0) + --claude-binary Path to Claude binary + --claude-home Path to Claude home directory + --help Show help message + --version Show version number +``` + +### Environment Variables + +- `PORT` - Server port +- `HOST` - Server host +- `CLAUDE_BINARY` - Path to Claude binary +- `CLAUDE_HOME` - Path to Claude home directory + +### Configuration Object + +You can also configure the server programmatically: + +```typescript +import { ClaudiaServer } from './server.js'; + +const server = new ClaudiaServer({ + port: 3000, + host: '0.0.0.0', + cors_origin: ['http://localhost:3000'], + max_concurrent_sessions: 10, + session_timeout_ms: 300000, + claude_binary_path: '/usr/local/bin/claude', + claude_home_dir: '/custom/claude/home', +}); + +await server.start(); +``` + +## Examples + +### Basic Usage with curl + +```bash +# Start a new Claude session +curl -X POST http://localhost:3000/api/claude/execute \\ + -H "Content-Type: application/json" \\ + -d '{ + "project_path": "/path/to/project", + "prompt": "Help me write a REST API", + "model": "claude-3-5-sonnet-20241022" + }' + +# List running sessions +curl http://localhost:3000/api/claude/sessions/running + +# Get session history +curl http://localhost:3000/api/claude/sessions/550e8400-e29b-41d4-a716-446655440000/history +``` + +### JavaScript Client Example + +```javascript +// HTTP API usage +const response = await fetch('http://localhost:3000/api/claude/execute', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + project_path: '/path/to/project', + prompt: 'Help me with this code', + model: 'claude-3-5-sonnet-20241022' + }) +}); + +const result = await response.json(); +const sessionId = result.data.session_id; + +// WebSocket usage for real-time streaming +const ws = new WebSocket('ws://localhost:3000/ws'); + +ws.onopen = () => { + // Subscribe to session updates + ws.send(JSON.stringify({ + type: 'subscribe', + session_id: sessionId + })); +}; + +ws.onmessage = (event) => { + const message = JSON.parse(event.data); + + if (message.type === 'claude_stream') { + console.log('Claude response:', message.data.content); + } +}; +``` + +### Python Client Example + +```python +import requests +import websocket +import json + +# HTTP API usage +response = requests.post('http://localhost:3000/api/claude/execute', json={ + 'project_path': '/path/to/project', + 'prompt': 'Help me with this code', + 'model': 'claude-3-5-sonnet-20241022' +}) + +session_id = response.json()['data']['session_id'] + +# WebSocket usage +def on_message(ws, message): + data = json.loads(message) + if data['type'] == 'claude_stream': + print(f"Claude: {data['data']['content']}") + +def on_open(ws): + # Subscribe to session + ws.send(json.dumps({ + 'type': 'subscribe', + 'session_id': session_id + })) + +ws = websocket.WebSocketApp('ws://localhost:3000/ws', + on_message=on_message, + on_open=on_open) +ws.run_forever() +``` + +## Troubleshooting + +### Common Issues + +#### Claude Binary Not Found +``` +Error: Claude binary not found. Please install Claude Code CLI. +``` + +**Solution:** Install Claude Code CLI and ensure it's in your PATH, or specify the path with `--claude-binary`. + +#### Permission Denied +``` +Error: EACCES: permission denied, spawn claude +``` + +**Solution:** Ensure the Claude binary has execute permissions: +```bash +chmod +x /path/to/claude +``` + +#### Port Already in Use +``` +Error: listen EADDRINUSE: address already in use :::3000 +``` + +**Solution:** Use a different port: +```bash +claudia-server --port 3001 +``` + +#### WebSocket Connection Failed +**Solution:** Check that the server is running and the WebSocket endpoint is accessible. Ensure firewalls aren't blocking the connection. + +### Debug Mode + +Set `NODE_ENV=development` for more detailed error messages and logging: + +```bash +NODE_ENV=development claudia-server +``` + +### Logs + +The server uses Morgan for HTTP request logging and console logging for application events. All logs go to stdout/stderr. + +### Health Checks + +Use the health endpoint to verify the server is running: + +```bash +curl http://localhost:3000/api/status/health +``` + +--- + +For more information and updates, visit the [Claudia GitHub repository](https://github.com/getAsterisk/claudia). \ No newline at end of file diff --git a/claudia-server/examples/README.md b/claudia-server/examples/README.md new file mode 100644 index 000000000..3e369a3ca --- /dev/null +++ b/claudia-server/examples/README.md @@ -0,0 +1,132 @@ +# Claudia Server Examples + +This directory contains example clients and usage demonstrations for the Claudia Server. + +## Examples Included + +### JavaScript/Node.js Client (`javascript/client.js`) +A complete Node.js client that demonstrates: +- HTTP API usage +- WebSocket streaming +- Error handling +- Session management + +**Usage:** +```bash +cd examples/javascript +npm install ws # Install WebSocket dependency +node client.js +``` + +### Python Client (`python/client.py`) +An async Python client using aiohttp and websockets: +- Async/await pattern +- Context managers for cleanup +- Type hints +- Error handling + +**Requirements:** +```bash +pip install aiohttp websockets +``` + +**Usage:** +```bash +cd examples/python +python client.py +``` + +### curl Examples (`curl/api-examples.sh`) +A shell script demonstrating all REST API endpoints using curl: +- Health checks +- Session management +- Project operations +- Error scenarios + +**Usage:** +```bash +cd examples/curl +./api-examples.sh +``` + +## Environment Variables + +All examples support these environment variables: + +- `CLAUDIA_SERVER_URL` - Server URL (default: http://localhost:3000) + +Example: +```bash +export CLAUDIA_SERVER_URL=http://myserver:8080 +node client.js +``` + +## Common Patterns + +### 1. Basic Session Flow +```javascript +// Start session +const sessionId = await client.startSession(projectPath, prompt, model); + +// Connect WebSocket for streaming +await client.connectWebSocket(sessionId); + +// Handle streaming responses +client.on('claude_stream', (data) => { + console.log(data.content); +}); +``` + +### 2. Error Handling +```javascript +try { + const sessionId = await client.startSession(projectPath, prompt, model); +} catch (error) { + if (error.message.includes('CLAUDE_NOT_FOUND')) { + console.log('Please install Claude Code CLI'); + } else { + console.error('Unexpected error:', error); + } +} +``` + +### 3. Session Management +```javascript +// List running sessions +const sessions = await client.getRunningSessions(); + +// Cancel specific session +await client.cancelSession(sessionId); + +// Get session history +const history = await client.getSessionHistory(sessionId); +``` + +## WebSocket Message Types + +### Client → Server +- `subscribe` - Subscribe to session updates +- `unsubscribe` - Unsubscribe from session + +### Server → Client +- `status` - Connection/subscription status +- `claude_stream` - Claude response data +- `error` - Error messages + +## Integration Tips + +1. **Health Checks**: Always check `/api/status/health` before starting sessions +2. **Error Handling**: Wrap API calls in try-catch blocks +3. **WebSocket Reconnection**: Implement reconnection logic for production use +4. **Rate Limiting**: Respect the max_concurrent_sessions limit +5. **Cleanup**: Always close WebSocket connections when done + +## Production Considerations + +1. **Authentication**: Add authentication for production deployments +2. **HTTPS/WSS**: Use secure connections in production +3. **Monitoring**: Monitor health endpoints and WebSocket connections +4. **Logging**: Implement proper logging for debugging +5. **Error Recovery**: Handle network failures gracefully + +For more detailed documentation, see the main README.md file. \ No newline at end of file diff --git a/claudia-server/examples/curl/api-examples.sh b/claudia-server/examples/curl/api-examples.sh new file mode 100755 index 000000000..ba87466c1 --- /dev/null +++ b/claudia-server/examples/curl/api-examples.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +# Claudia Server curl examples +# These examples demonstrate the REST API functionality + +SERVER_URL="${CLAUDIA_SERVER_URL:-http://localhost:3000}" + +echo "🚀 Claudia Server API Examples" +echo "Server URL: $SERVER_URL" +echo + +# Check server health +echo "1. 🩺 Health Check" +curl -s "$SERVER_URL/api/status/health" | jq '.' +echo + +# Get server info +echo "2. ℹ️ Server Info" +curl -s "$SERVER_URL/api/status/info" | jq '.' +echo + +# Check Claude version +echo "3. 🤖 Claude Version" +curl -s "$SERVER_URL/api/claude/version" | jq '.' +echo + +# List projects +echo "4. 📁 List Projects" +curl -s "$SERVER_URL/api/projects" | jq '.' +echo + +# Create a new project (optional) +# echo "5. 📂 Create Project" +# curl -s -X POST "$SERVER_URL/api/projects" \ +# -H "Content-Type: application/json" \ +# -d '{"path": "/tmp/test-project"}' | jq '.' +# echo + +# Start a Claude session +echo "5. 🚀 Start Claude Session" +PROJECT_PATH="$(pwd)" +SESSION_RESPONSE=$(curl -s -X POST "$SERVER_URL/api/claude/execute" \ + -H "Content-Type: application/json" \ + -d "{ + \"project_path\": \"$PROJECT_PATH\", + \"prompt\": \"Help me understand this project structure\", + \"model\": \"claude-3-5-sonnet-20241022\" + }") + +echo "$SESSION_RESPONSE" | jq '.' + +# Extract session ID for further examples +SESSION_ID=$(echo "$SESSION_RESPONSE" | jq -r '.data.session_id // empty') + +if [ -n "$SESSION_ID" ]; then + echo + echo "6. 📊 Get Session Info" + curl -s "$SERVER_URL/api/claude/sessions/$SESSION_ID" | jq '.' + echo + + echo "7. 🏃 List Running Sessions" + curl -s "$SERVER_URL/api/claude/sessions/running" | jq '.' + echo + + # Wait a moment for some output + echo "8. ⏳ Waiting 5 seconds for some output..." + sleep 5 + + echo "9. 📜 Get Session History (first 500 chars)" + curl -s "$SERVER_URL/api/claude/sessions/$SESSION_ID/history" | jq -r '.data.history' | head -c 500 + echo "..." + echo + + echo "10. 🛑 Cancel Session" + curl -s -X POST "$SERVER_URL/api/claude/cancel/$SESSION_ID" | jq '.' + echo +else + echo "❌ Failed to create session, skipping session-specific examples" +fi + +echo "✅ Examples completed!" +echo +echo "💡 For real-time streaming, use WebSocket connection:" +echo " ws://${SERVER_URL#http://}/ws" +echo +echo "📖 For more examples, see:" +echo " - JavaScript: examples/javascript/client.js" +echo " - Python: examples/python/client.py" \ No newline at end of file diff --git a/claudia-server/examples/javascript/client.js b/claudia-server/examples/javascript/client.js new file mode 100644 index 000000000..4a9a78cbe --- /dev/null +++ b/claudia-server/examples/javascript/client.js @@ -0,0 +1,237 @@ +#!/usr/bin/env node + +/** + * JavaScript/Node.js example client for Claudia Server + * + * This example demonstrates: + * - Starting a Claude session + * - Real-time streaming via WebSocket + * - Handling different message types + * - Error handling + */ + +import WebSocket from 'ws'; + +const SERVER_URL = process.env.CLAUDIA_SERVER_URL || 'http://localhost:3000'; +const WS_URL = SERVER_URL.replace('http', 'ws') + '/ws'; + +class ClaudiaClient { + constructor(serverUrl = SERVER_URL) { + this.serverUrl = serverUrl; + this.ws = null; + } + + /** + * Start a new Claude session + */ + async startSession(projectPath, prompt, model = 'claude-3-5-sonnet-20241022') { + const response = await fetch(`${this.serverUrl}/api/claude/execute`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + project_path: projectPath, + prompt: prompt, + model: model + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(`Failed to start session: ${error.error}`); + } + + const result = await response.json(); + return result.data.session_id; + } + + /** + * Connect to WebSocket and subscribe to session + */ + async connectWebSocket(sessionId) { + return new Promise((resolve, reject) => { + const wsUrl = this.serverUrl.replace(/^http(s?):\/\//, 'ws$1://') + '/ws'; + this.ws = new WebSocket(wsUrl); + this.ws.on('open', () => { + console.log('📡 Connected to WebSocket'); + + // Subscribe to session + this.ws.send(JSON.stringify({ + type: 'subscribe', + session_id: sessionId + })); + + resolve(); + }); + + this.ws.on('message', (data) => { + try { + const message = JSON.parse(data.toString()); + this.handleMessage(message); + } catch (error) { + console.error('Failed to parse WebSocket message:', error); + } + }); + + this.ws.on('error', (error) => { + console.error('WebSocket error:', error); + reject(error); + }); + + this.ws.on('close', () => { + console.log('📡 WebSocket connection closed'); + }); + }); + } + + /** + * Handle incoming WebSocket messages + */ + handleMessage(message) { + switch (message.type) { + case 'status': + console.log('📊 Status:', message.data.status); + break; + + case 'claude_stream': + this.handleClaudeStream(message.data); + break; + + case 'error': + console.error('❌ Error:', message.data.error); + break; + + default: + console.log('📩 Unknown message type:', message.type); + } + } + + /** + * Handle Claude streaming messages + */ + handleClaudeStream(data) { + switch (data.type) { + case 'start': + console.log('🤖 Claude started responding...'); + break; + + case 'partial': + // Stream the content as it comes + if (data.content) { + process.stdout.write(data.content); + } + break; + + case 'complete': + console.log('\\n✅ Claude completed response'); + break; + + case 'error': + console.error('\\n❌ Claude error:', data.content); + break; + + default: + console.log('\\n📝 Claude output:', data.content || data); + } + } + + /** + * Get list of running sessions + */ + async getRunningSessions() { + const response = await fetch(`${this.serverUrl}/api/claude/sessions/running`); + + if (!response.ok) { + const error = await response.json(); + throw new Error(`Failed to get running sessions: ${error.error}`); + } + + const result = await response.json(); + return result.data; + } + + /** + * Cancel a session + */ + async cancelSession(sessionId) { + const response = await fetch(`${this.serverUrl}/api/claude/cancel/${sessionId}`, { + method: 'POST' + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(`Failed to cancel session: ${error.error}`); + } + + const result = await response.json(); + return result.data.cancelled; + } + + /** + * Close WebSocket connection + */ + close() { + if (this.ws) { + this.ws.close(); + } + } +} + +/** + * Example entrypoint demonstrating end-to-end usage of ClaudiaClient. + * + * Performs a health check against the Claudia server, starts a Claude session + * for the current working directory, connects to the server WebSocket to stream + * real-time responses, and installs a SIGINT handler for graceful shutdown. + * + * This is an example/demo helper — it performs network I/O, logs status to stdout, + * and exits the process on error or when the user interrupts (Ctrl+C). + * + * @returns {Promise} Resolves when the setup completes; the process may continue running to receive streamed responses. + */ +async function example() { + const client = new ClaudiaClient(); + + try { + // Check if server is healthy + const healthResponse = await fetch(`${client.serverUrl}/api/status/health`); + if (!healthResponse.ok) { + throw new Error('Server is not healthy'); + } + console.log('✅ Server is healthy'); + + // Get current directory as project path + const projectPath = process.cwd(); + console.log(`📁 Using project path: ${projectPath}`); + + // Start a new Claude session + console.log('🚀 Starting Claude session...'); + const sessionId = await client.startSession( + projectPath, + 'Help me understand the structure of this project and suggest improvements.', + 'claude-3-5-sonnet-20241022' + ); + console.log(`🎯 Session started: ${sessionId}`); + + // Connect WebSocket for real-time streaming + await client.connectWebSocket(sessionId); + + // Keep the connection alive + console.log('👂 Listening for Claude responses... (Press Ctrl+C to exit)'); + + // Handle graceful shutdown + process.on('SIGINT', () => { + console.log('\\n🛑 Shutting down...'); + client.close(); + process.exit(0); + }); + + } catch (error) { + console.error('❌ Error:', error.message); + process.exit(1); + } +} + +// Run example if this file is executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + example(); +} \ No newline at end of file diff --git a/claudia-server/examples/python/client.py b/claudia-server/examples/python/client.py new file mode 100644 index 000000000..bd7e0aa04 --- /dev/null +++ b/claudia-server/examples/python/client.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 + +""" +Python example client for Claudia Server + +This example demonstrates: +- Starting a Claude session +- Real-time streaming via WebSocket +- Handling different message types +- Error handling +""" + +import asyncio +import json +import os +import sys +import signal +import aiohttp +import websockets +from typing import Optional + +class ClaudiaClient: + def __init__(self, server_url: str = None): + """ + Initialize a ClaudiaClient. + + If server_url is not provided, reads CLAUDIA_SERVER_URL from the environment and defaults to "http://localhost:3000". + Derives the WebSocket URL by replacing the HTTP scheme with "ws" and appending "/ws", and initializes session and websocket attributes. + + Parameters: + server_url (str, optional): Base URL of the Claudia Server (e.g. "http://localhost:3000"). If omitted, the + CLAUDIA_SERVER_URL environment variable is used; if that is unset, "http://localhost:3000" is used. + """ + self.server_url = server_url or os.environ.get('CLAUDIA_SERVER_URL', 'http://localhost:3000') + self.ws_url = self.server_url.replace('http', 'ws') + '/ws' + self.session = None + self.websocket = None + + async def __aenter__(self): + """ + Enter async context: create an aiohttp ClientSession and return the client instance. + + Initializes and stores an aiohttp.ClientSession on self.session for use by other methods. Returns self so the class can be used as an asynchronous context manager. + """ + self.session = aiohttp.ClientSession() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """ + Close the client's HTTP session and WebSocket when exiting the async context manager. + + If an HTTP session or WebSocket is open, they are closed asynchronously. The method does not suppress exceptions raised in the managed block (it returns None). + """ + if self.session: + await self.session.close() + if self.websocket: + await self.websocket.close() + + async def start_session(self, project_path: str, prompt: str, model: str = 'claude-3-5-sonnet-20241022') -> str: + """ + Start a new Claude session on the configured Claudia Server. + + Sends a POST to /api/claude/execute with the project path, prompt, and model. + On success returns the created session's ID. On non-200 responses raises an + Exception with the server-provided error message. + + Parameters: + project_path (str): Filesystem path of the project to run the prompt against. + prompt (str): The text prompt to send to Claude. + model (str): Model identifier to use for the session (defaults to 'claude-3-5-sonnet-20241022'). + + Returns: + str: The newly created session_id. + + Raises: + Exception: If the server responds with a non-200 status; message includes server error. + """ + url = f"{self.server_url}/api/claude/execute" + data = { + 'project_path': project_path, + 'prompt': prompt, + 'model': model + } + + async with self.session.post(url, json=data) as response: + if response.status != 200: + error = await response.json() + raise Exception(f"Failed to start session: {error['error']}") + + result = await response.json() + return result['data']['session_id'] + + async def connect_websocket(self, session_id: str): + """ + Establish a WebSocket connection to the server, subscribe to a session, and stream incoming messages to the message handler. + + Opens a connection to self.ws_url and stores the WebSocket in self.websocket, sends a JSON subscribe message for the given session, then iterates over incoming text messages, parsing each as JSON and delegating to self.handle_message. JSON parsing errors and handler exceptions are caught and logged; connection errors from establishing the WebSocket may propagate. + Parameters: + session_id (str): The server session identifier to subscribe to. + """ + self.websocket = await websockets.connect(self.ws_url) + print("📡 Connected to WebSocket") + + # Subscribe to session + subscribe_message = { + 'type': 'subscribe', + 'session_id': session_id + } + await self.websocket.send(json.dumps(subscribe_message)) + + # Start listening for messages + async for message in self.websocket: + try: + data = json.loads(message) + await self.handle_message(data) + except json.JSONDecodeError as e: + print(f"Failed to parse WebSocket message: {e}") + except Exception as e: + print(f"Error handling message: {e}") + + async def handle_message(self, message: dict): + """ + Dispatch and process a single incoming WebSocket message from the server. + + Recognizes message types in the `message['type']` field: + - "status": prints a short status line from `message['data']['status']`. + - "claude_stream": forwards `message['data']` to `handle_claude_stream` for streaming output handling. + - "error": prints the error text from `message['data']['error']`. + - any other value: prints an "unknown message type" notice. + + Parameters: + message (dict): Parsed JSON message with at least a `type` key and a `data` payload whose structure depends on `type`. + """ + message_type = message.get('type') + + if message_type == 'status': + print(f"📊 Status: {message['data']['status']}") + elif message_type == 'claude_stream': + await self.handle_claude_stream(message['data']) + elif message_type == 'error': + print(f"❌ Error: {message['data']['error']}") + else: + print(f"📩 Unknown message type: {message_type}") + + async def handle_claude_stream(self, data: dict): + """ + Handle incoming Claude streaming events and render them to stdout. + + The `data` payload is a dictionary that must include a 'type' field indicating the stream event: + - 'start' : indicates the model has begun producing a response; prints a start notice. + - 'partial' : contains incremental text under 'content'; prints text without a trailing newline to stream output progressively. + - 'complete' : signals the end of the response; prints a newline and a completion notice. + - 'error' : contains an error message under 'content'; prints the error with a leading newline. + - other : prints the provided 'content' (or the full payload) as a generic Claude output. + + This function has no return value; it side-effects by printing to stdout. + """ + stream_type = data.get('type') + + if stream_type == 'start': + print("🤖 Claude started responding...") + elif stream_type == 'partial': + # Stream the content as it comes + content = data.get('content', '') + if content: + print(content, end='', flush=True) + elif stream_type == 'complete': + print("\\n✅ Claude completed response") + elif stream_type == 'error': + print(f"\\n❌ Claude error: {data.get('content', '')}") + else: + print(f"\\n📝 Claude output: {data.get('content', data)}") + + async def get_running_sessions(self) -> list: + """ + Return the list of currently running Claude sessions. + + Returns: + list: Session objects parsed from the server response `data` field. + + Raises: + Exception: If the server responds with a non-200 status; message is taken from the server's `error` field. + """ + url = f"{self.server_url}/api/claude/sessions/running" + + async with self.session.get(url) as response: + if response.status != 200: + error = await response.json() + raise Exception(f"Failed to get running sessions: {error['error']}") + + result = await response.json() + return result['data'] + + async def cancel_session(self, session_id: str) -> bool: + """ + Cancel a running Claude session on the server. + + Sends a POST request to /api/claude/cancel/{session_id} and returns whether the session was successfully cancelled. + + Parameters: + session_id (str): The server-side identifier of the session to cancel. + + Returns: + bool: True if the server reports the session was cancelled, False otherwise. + + Raises: + Exception: If the HTTP response status is not 200; the exception message contains the server-provided error. + """ + url = f"{self.server_url}/api/claude/cancel/{session_id}" + + async with self.session.post(url) as response: + if response.status != 200: + error = await response.json() + raise Exception(f"Failed to cancel session: {error['error']}") + + result = await response.json() + return result['data']['cancelled'] + + async def check_health(self) -> dict: + """ + Check the Claudia Server health status. + + Performs a GET request to the server's /api/status/health endpoint and returns the parsed JSON health payload on success. + + Returns: + dict: The JSON response body describing server health. + + Raises: + Exception: If the server responds with a non-200 status code. + """ + url = f"{self.server_url}/api/status/health" + + async with self.session.get(url) as response: + if response.status != 200: + raise Exception("Server is not healthy") + + return await response.json() + +async def example(): + """ + Run a self-contained example demonstrating ClaudiaClient usage. + + This coroutine exercises the end-to-end flow against a Claudia Server: + - Opens a ClaudiaClient session context. + - Verifies server health. + - Uses the current working directory as the project path. + - Starts a Claude session with a sample prompt and model, printing the returned session ID. + - Connects to the server WebSocket to stream real-time responses until interrupted. + + Handles KeyboardInterrupt by printing a shutdown message; on other exceptions it prints the error and exits the process with code 1. + """ + async with ClaudiaClient() as client: + try: + # Check if server is healthy + health = await client.check_health() + print("✅ Server is healthy") + + # Get current directory as project path + project_path = os.getcwd() + print(f"📁 Using project path: {project_path}") + + # Start a new Claude session + print("🚀 Starting Claude session...") + session_id = await client.start_session( + project_path, + "Help me understand the structure of this project and suggest improvements.", + "claude-3-5-sonnet-20241022" + ) + print(f"🎯 Session started: {session_id}") + + # Connect WebSocket for real-time streaming + print("👂 Listening for Claude responses... (Press Ctrl+C to exit)") + await client.connect_websocket(session_id) + + except KeyboardInterrupt: + print("\\n🛑 Shutting down...") + except Exception as error: + print(f"❌ Error: {error}") + sys.exit(1) + +if __name__ == "__main__": + # Handle graceful shutdown + def signal_handler(sig, frame): + """ + Handle an OS signal by printing a shutdown message and exiting the process with status code 0. + + Parameters: + sig: Signal number received. + frame: Current stack frame (unused). + """ + print("\\n🛑 Shutting down...") + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + # Run the example + asyncio.run(example()) \ No newline at end of file diff --git a/claudia-server/package-lock.json b/claudia-server/package-lock.json new file mode 100644 index 000000000..3a8689ace --- /dev/null +++ b/claudia-server/package-lock.json @@ -0,0 +1,8146 @@ +{ + "name": "claudia-server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "claudia-server", + "version": "1.0.0", + "license": "AGPL-3.0", + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^11.1.0", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "helmet": "^7.1.0", + "joi": "^17.11.0", + "morgan": "^1.10.0", + "node-cron": "^3.0.3", + "sqlite3": "^5.1.6", + "uuid": "^9.0.1", + "ws": "^8.14.2" + }, + "bin": { + "claudia-cli": "dist/cli.js", + "claudia-server": "dist/index.js" + }, + "devDependencies": { + "@types/compression": "^1.7.5", + "@types/cors": "^2.8.16", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.7", + "@types/morgan": "^1.9.9", + "@types/node": "^20.8.0", + "@types/node-cron": "^3.0.11", + "@types/sqlite3": "^3.1.11", + "@types/uuid": "^9.0.7", + "@types/ws": "^8.5.8", + "@typescript-eslint/eslint-plugin": "^6.9.0", + "@typescript-eslint/parser": "^6.9.0", + "eslint": "^8.52.0", + "jest": "^29.7.0", + "prettier": "^3.0.3", + "ts-jest": "^29.1.1", + "tsx": "^3.14.0", + "typescript": "^5.2.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "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/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", + "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/core/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/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@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.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@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-compilation-targets/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/@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.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "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.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "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.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + }, + "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.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "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-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.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "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-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.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "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/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "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/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/config-array/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/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "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/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@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/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/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/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.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "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/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "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/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "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/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "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/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/morgan": { + "version": "1.9.10", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.10.tgz", + "integrity": "sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-cron": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/node-cron/-/node-cron-3.0.11.tgz", + "integrity": "sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sqlite3": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@types/sqlite3/-/sqlite3-3.1.11.tgz", + "integrity": "sha512-KYF+QgxAnnAh7DWPdNDroxkDI3/MspH1NMx6m/N/6fT1G6+jvsw4/ZePt8R8cr7ta58aboeTfYFBDxTJ5yv15w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "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/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "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-escapes/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/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "devOptional": 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==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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/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/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "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/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "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": { + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "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": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "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/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", + "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/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/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "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/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "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/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "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/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/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.205", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.205.tgz", + "integrity": "sha512-gBtbT7IgOHu7CwdtIiXwbNRD1l6oG6GAyanmwMCLVqaoGy92Jfe1dSHLiSj8xUEZNxOTIVlXuaAalMMD+S4y0w==", + "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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "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-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/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/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "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/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "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/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "devOptional": 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==", + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "devOptional": 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/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "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/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/helmet": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", + "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==", + "license": "MIT", + "engines": { + "node": ">=16.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/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "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/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "devOptional": 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==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, + "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.", + "devOptional": 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==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "devOptional": 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-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "devOptional": 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-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": "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/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/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-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-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-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-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-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-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-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-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/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-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/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "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": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "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/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "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/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "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/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "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/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-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/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.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/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "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/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/morgan": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "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/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-cron": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", + "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", + "license": "ISC", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-cron/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "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.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "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/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.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==", + "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/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "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/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "devOptional": 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-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "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/pkg-dir/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/pkg-dir/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/pkg-dir/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/pkg-dir/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/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "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/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", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "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/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "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-cwd/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-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "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/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "devOptional": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "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/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "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/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "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-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/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/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/stack-utils/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/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "devOptional": 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/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "devOptional": 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-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/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/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "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/test-exclude/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/test-exclude/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/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "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==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", + "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", + "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.2", + "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/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/tsx": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.14.0.tgz", + "integrity": "sha512-xHtFaKtHxM9LOklMmJdI3BEnQq/D5F73Of2E1GDrITi9sgoVkvIsrQUTY1G8FlmGtA+awCI4EBlTRRYxkL2sRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.18.20", + "get-tsconfig": "^4.7.2", + "source-map-support": "^0.5.21" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "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-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "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/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "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" + } + } + } +} diff --git a/claudia-server/package.json b/claudia-server/package.json new file mode 100644 index 000000000..abeae4fd9 --- /dev/null +++ b/claudia-server/package.json @@ -0,0 +1,74 @@ +{ + "name": "claudia-server", + "version": "1.0.0", + "description": "Standalone TypeScript server for Claude Code integration", + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc", + "start": "node dist/index.js", + "cli": "node dist/cli.js", + "prepare": "npm run build", + "test": "jest", + "lint": "eslint src --ext .ts", + "format": "prettier --write src/**/*.ts", + "clean": "rm -rf dist" + }, + "type": "module", + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc", + "start": "node dist/index.js", + "cli": "node dist/cli.js", + "test": "jest", + "lint": "eslint src --ext .ts", + "format": "prettier --write src/**/*.ts", + "clean": "rm -rf dist" + }, + "keywords": [ + "claude", + "claude-code", + "ai", + "api", + "server", + "typescript" + ], + "author": "Claudia Project", + "license": "AGPL-3.0", + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^11.1.0", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "helmet": "^7.1.0", + "joi": "^17.11.0", + "morgan": "^1.10.0", + "node-cron": "^3.0.3", + "sqlite3": "^5.1.6", + "uuid": "^9.0.1", + "ws": "^8.14.2" + }, + "devDependencies": { + "@types/compression": "^1.7.5", + "@types/cors": "^2.8.16", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.7", + "@types/morgan": "^1.9.9", + "@types/node": "^20.8.0", + "@types/node-cron": "^3.0.11", + "@types/sqlite3": "^3.1.11", + "@types/uuid": "^9.0.7", + "@types/ws": "^8.5.8", + "@typescript-eslint/eslint-plugin": "^6.9.0", + "@typescript-eslint/parser": "^6.9.0", + "eslint": "^8.52.0", + "jest": "^29.7.0", + "prettier": "^3.0.3", + "ts-jest": "^29.1.1", + "tsx": "^3.14.0", + "typescript": "^5.2.2" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/claudia-server/src/cli.ts b/claudia-server/src/cli.ts new file mode 100644 index 000000000..23cc3e94d --- /dev/null +++ b/claudia-server/src/cli.ts @@ -0,0 +1,254 @@ +#!/usr/bin/env node + +/** + * Claudia Server CLI Tool + * Provides easy command-line interface for interacting with Claudia Server + */ + +import { program } from 'commander'; +import { ClaudiaServer } from './server.js'; +import { readFileSync } from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Read package.json for version +const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')); + +program + .name('claudia-server') + .description('Standalone TypeScript server for Claude Code integration') + .version(packageJson.version); + +// Server commands +const serverCmd = program + .command('server') + .description('Server management commands'); + +serverCmd + .command('start') + .description('Start the Claudia server') + .option('-p, --port ', 'Server port', '3000') + .option('-h, --host ', 'Server host', '0.0.0.0') + .option('--claude-binary ', 'Path to Claude binary') + .option('--claude-home ', 'Path to Claude home directory') + .option('-d, --daemon', 'Run as daemon (background process)') + .action(async (options) => { + try { + const config = { + port: parseInt(options.port, 10), + host: options.host, + claude_binary_path: options.claudeBinary, + claude_home_dir: options.claudeHome, + }; + + const server = new ClaudiaServer(config); + + if (options.daemon) { + // For daemon mode, we would need to implement proper daemonization + console.log('Daemon mode not yet implemented. Starting in foreground...'); + } + + await server.start(); + + // Keep running until interrupted + process.on('SIGINT', async () => { + console.log('\\nShutting down server...'); + await server.stop(); + process.exit(0); + }); + + } catch (error) { + console.error('Failed to start server:', (error as Error).message); + process.exit(1); + } + }); + +// Client commands +const clientCmd = program + .command('client') + .description('Client commands for interacting with Claudia server') + .option('-s, --server ', 'Server URL', 'http://localhost:3000'); + +clientCmd + .command('health') + .description('Check server health') + .action(async (options, cmd) => { + const serverUrl = cmd.parent.opts().server; + try { + const response = await fetch(`${serverUrl}/api/status/health`); + const result = await response.json() as any; + + if (result.success) { + console.log('✅ Server is healthy'); + console.log(` Uptime: ${result.data.uptime} seconds`); + console.log(` Memory: ${Math.round(result.data.memory.heapUsed / 1024 / 1024)}MB`); + } else { + console.log('❌ Server is unhealthy'); + process.exit(1); + } + } catch (error) { + console.error('❌ Cannot connect to server:', (error as Error).message); + process.exit(1); + } + }); + +clientCmd + .command('execute ') + .description('Execute Claude Code with a prompt') + .option('-p, --project ', 'Project path', process.cwd()) + .option('-m, --model ', 'Claude model', 'claude-3-5-sonnet-20241022') + .option('-w, --watch', 'Watch for real-time output via WebSocket') + .action(async (prompt, options, cmd) => { + const serverUrl = cmd.parent.opts().server; + + try { + const response = await fetch(`${serverUrl}/api/claude/execute`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + project_path: options.project, + prompt: prompt, + model: options.model + }) + }); + + const result = await response.json() as any; + + if (result.success) { + console.log(`🚀 Session started: ${result.data.session_id}`); + + if (options.watch) { + console.log('👂 Watching for real-time output...'); + // Here we would implement WebSocket watching + // For now, just show the session ID + } else { + console.log('💡 Use --watch flag to see real-time output'); + } + } else { + console.error('❌ Failed to start session:', result.error); + process.exit(1); + } + } catch (error) { + console.error('❌ Error:', (error as Error).message); + process.exit(1); + } + }); + +clientCmd + .command('sessions') + .description('List running sessions') + .action(async (options, cmd) => { + const serverUrl = cmd.parent.opts().server; + + try { + const response = await fetch(`${serverUrl}/api/claude/sessions/running`); + const result = await response.json() as any; + + if (result.success) { + const sessions = result.data; + + if (sessions.length === 0) { + console.log('📭 No running sessions'); + } else { + console.log(`🏃 Running sessions (${sessions.length}):`); + sessions.forEach((session: any) => { + const sessionType = session.process_type.ClaudeSession ? + session.process_type.ClaudeSession.session_id : + 'Unknown'; + console.log(` ${sessionType} - ${session.task.substring(0, 50)}...`); + console.log(` Project: ${session.project_path}`); + console.log(` Model: ${session.model}`); + console.log(` Started: ${session.started_at}`); + console.log(); + }); + } + } else { + console.error('❌ Failed to get sessions:', result.error); + process.exit(1); + } + } catch (error) { + console.error('❌ Error:', (error as Error).message); + process.exit(1); + } + }); + +clientCmd + .command('cancel ') + .description('Cancel a running session') + .action(async (sessionId, options, cmd) => { + const serverUrl = cmd.parent.opts().server; + + try { + const response = await fetch(`${serverUrl}/api/claude/cancel/${sessionId}`, { + method: 'POST' + }); + + const result = await response.json() as any; + + if (result.success) { + if (result.data.cancelled) { + console.log(`✅ Session ${sessionId} cancelled`); + } else { + console.log(`⚠️ Session ${sessionId} was not running`); + } + } else { + console.error('❌ Failed to cancel session:', result.error); + process.exit(1); + } + } catch (error) { + console.error('❌ Error:', (error as Error).message); + process.exit(1); + } + }); + +// Project commands +const projectCmd = program + .command('projects') + .description('Project management commands') + .option('-s, --server ', 'Server URL', 'http://localhost:3000'); + +projectCmd + .command('list') + .description('List all projects') + .action(async (options, cmd) => { + const serverUrl = cmd.parent.opts().server; + + try { + const response = await fetch(`${serverUrl}/api/projects`); + const result = await response.json() as any; + + if (result.success) { + const projects = result.data; + + if (projects.length === 0) { + console.log('📭 No projects found'); + } else { + console.log(`📁 Projects (${projects.length}):`); + projects.forEach((project: any) => { + console.log(` ${project.id}`); + console.log(` Path: ${project.path}`); + console.log(` Sessions: ${project.sessions.length}`); + console.log(` Created: ${new Date(project.created_at * 1000).toLocaleString()}`); + console.log(); + }); + } + } else { + console.error('❌ Failed to get projects:', result.error); + process.exit(1); + } + } catch (error) { + console.error('❌ Error:', (error as Error).message); + process.exit(1); + } + }); + +// Add version and help +program.parse(); + +// Show help if no command provided +if (!process.argv.slice(2).length) { + program.outputHelp(); +} \ No newline at end of file diff --git a/claudia-server/src/index.ts b/claudia-server/src/index.ts new file mode 100644 index 000000000..6a1e75723 --- /dev/null +++ b/claudia-server/src/index.ts @@ -0,0 +1,181 @@ +#!/usr/bin/env node + +import { ClaudiaServer } from './server.js'; +import type { ServerConfig } from './types/index.js'; + +/** + * Parse command-line arguments into a partial ServerConfig. + * + * Recognized options: + * - `--port`, `-p ` — sets `port` + * - `--host`, `-h ` — sets `host` + * - `--claude-binary ` — sets `claude_binary_path` + * - `--claude-home ` — sets `claude_home_dir` + * - `--help` — prints help and exits (0) + * - `--version` — prints the version and exits (0) + * + * Options that take a value expect the value as the next argument and will + * ignore values that start with `-`. Unknown options beginning with `-` + * result in an error message and exit(1). + * + * Note: this function may call `process.exit()` as a side effect for help, + * version, or unrecognized option handling. + * + * @returns A Partial populated with any recognized CLI options. + */ +function parseArgs(): Partial { + const args = process.argv.slice(2); + const config: Partial = {}; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + const nextArg = args[i + 1]; + + switch (arg) { + case '--port': + case '-p': + if (nextArg && !nextArg.startsWith('-')) { + config.port = parseInt(nextArg, 10); + i++; + } + break; + case '--host': + case '-h': + if (nextArg && !nextArg.startsWith('-')) { + config.host = nextArg; + i++; + } + break; + case '--claude-binary': + if (nextArg && !nextArg.startsWith('-')) { + config.claude_binary_path = nextArg; + i++; + } + break; + case '--claude-home': + if (nextArg && !nextArg.startsWith('-')) { + config.claude_home_dir = nextArg; + i++; + } + break; + case '--help': + printHelp(); + process.exit(0); + break; + case '--version': + console.log('1.0.0'); + process.exit(0); + break; + default: + if (arg.startsWith('-')) { + console.error(`Unknown option: ${arg}`); + process.exit(1); + } + } + } + + return config; +} + +/** + * Print the command-line help/usage information for the Claudia Server CLI. + * + * Outputs usage, supported options, examples, relevant environment variables, + * and available API endpoints to the process standard output. + */ +function printHelp(): void { + console.log(` +Claudia Server - Standalone TypeScript server for Claude Code integration + +Usage: claudia-server [options] + +Options: + -p, --port Server port (default: 3000) + -h, --host Server host (default: 0.0.0.0) + --claude-binary Path to Claude binary (auto-detected if not specified) + --claude-home Path to Claude home directory (default: ~/.claude) + --help Show this help message + --version Show version number + +Examples: + claudia-server # Start with default settings + claudia-server --port 8080 # Start on port 8080 + claudia-server --host localhost --port 3001 # Start on localhost:3001 + claudia-server --claude-binary /usr/bin/claude # Use specific Claude binary + +Environment Variables: + PORT Server port (overridden by --port) + HOST Server host (overridden by --host) + CLAUDE_BINARY Claude binary path (overridden by --claude-binary) + CLAUDE_HOME Claude home directory (overridden by --claude-home) + +API Endpoints: + GET / Server info + GET /api/status/health Health check + GET /api/status/info Detailed server info + GET /api/claude/version Claude version info + POST /api/claude/execute Execute Claude Code + POST /api/claude/continue Continue Claude conversation + POST /api/claude/resume Resume Claude session + GET /api/projects List projects + POST /api/projects Create project + WS /ws WebSocket for real-time streaming + +For more information, visit: https://github.com/getAsterisk/claudia +`); +} + +/** + * Start the Claudia server using CLI arguments and environment variables. + * + * Parses command-line options, merges them with environment variables to construct a partial ServerConfig, + * instantiates and starts a ClaudiaServer, and logs the effective runtime configuration (port, host, + * CORS origins, max concurrent sessions, and optionally Claude binary/home paths). On startup failure + * the function logs the error and exits the process with code 1. + * + * @returns A promise that resolves when the server has started. + */ +async function main(): Promise { + try { + // Parse command line arguments + const cliConfig = parseArgs(); + + // Merge with environment variables + const config: Partial = { + port: cliConfig.port || (process.env.PORT ? parseInt(process.env.PORT, 10) : undefined), + host: cliConfig.host || process.env.HOST, + claude_binary_path: cliConfig.claude_binary_path || process.env.CLAUDE_BINARY, + claude_home_dir: cliConfig.claude_home_dir || process.env.CLAUDE_HOME, + }; + + // Create and start server + const server = new ClaudiaServer(config); + await server.start(); + + // Log configuration + const serverConfig = server.getConfig(); + console.log('Server Configuration:'); + console.log(` Port: ${serverConfig.port}`); + console.log(` Host: ${serverConfig.host}`); + console.log(` CORS Origins: ${serverConfig.cors_origin.join(', ')}`); + console.log(` Max Concurrent Sessions: ${serverConfig.max_concurrent_sessions}`); + if (serverConfig.claude_binary_path) { + console.log(` Claude Binary: ${serverConfig.claude_binary_path}`); + } + if (serverConfig.claude_home_dir) { + console.log(` Claude Home: ${serverConfig.claude_home_dir}`); + } + + } catch (error) { + console.error('Failed to start Claudia Server:', error); + process.exit(1); + } +} + +// Start the server if this file is run directly +if (import.meta.url === `file://${process.argv[1]}`) { + main().catch((error) => { + console.error('Unhandled error:', error); + process.exit(1); + }); +} \ No newline at end of file diff --git a/claudia-server/src/routes/claude.ts b/claudia-server/src/routes/claude.ts new file mode 100644 index 000000000..ee909e508 --- /dev/null +++ b/claudia-server/src/routes/claude.ts @@ -0,0 +1,276 @@ +import { Router } from 'express'; +import type { ClaudeService } from '../services/claude.js'; +import type { ProjectService } from '../services/project.js'; +import type { + ExecuteClaudeRequest, + ContinueClaudeRequest, + ResumeClaudeRequest, + SuccessResponse, + ErrorResponse +} from '../types/index.js'; + +/** + * Creates an Express Router with endpoints for managing and interacting with Claude code executions. + * + * The router exposes these routes: + * - GET /version — check Claude code version/installation status + * - POST /execute — start a new Claude execution (requires project_path, prompt, model) + * - POST /continue — continue an existing conversation (requires project_path, prompt, model) + * - POST /resume — resume a session (requires project_path, session_id, prompt, model) + * - POST /cancel/:sessionId — cancel a running execution + * - GET /sessions/running — list running Claude sessions + * - GET /sessions/:sessionId — get session information + * - GET /sessions/:sessionId/history — load session history/output + * + * All endpoints return a standardized SuccessResponse or ErrorResponse object with a timestamp and appropriate HTTP status codes for validation, not-found, and internal errors. + * + * @returns An Express Router configured with the Claude-related routes. + */ +export function createClaudeRoutes( + claudeService: ClaudeService, + projectService: ProjectService +): Router { + const router = Router(); + + /** + * Check Claude Code version and installation status + */ + router.get('/version', async (req, res) => { + try { + const versionStatus = await claudeService.checkClaudeVersion(); + + const response: SuccessResponse = { + success: true, + data: versionStatus, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'CLAUDE_VERSION_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Execute Claude Code with new prompt + */ + router.post('/execute', async (req, res) => { + try { + const request = req.body as ExecuteClaudeRequest; + + // Validate request + if (!request.project_path || !request.prompt || !request.model) { + const errorResponse: ErrorResponse = { + error: 'Missing required fields: project_path, prompt, model', + code: 'VALIDATION_ERROR', + timestamp: new Date().toISOString(), + }; + return res.status(400).json(errorResponse); + } + + const sessionId = await claudeService.executeClaudeCode(request); + + const response: SuccessResponse = { + success: true, + data: { session_id: sessionId }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'EXECUTION_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Continue existing Claude Code conversation + */ + router.post('/continue', async (req, res) => { + try { + const request = req.body as ContinueClaudeRequest; + + // Validate request + if (!request.project_path || !request.prompt || !request.model) { + const errorResponse: ErrorResponse = { + error: 'Missing required fields: project_path, prompt, model', + code: 'VALIDATION_ERROR', + timestamp: new Date().toISOString(), + }; + return res.status(400).json(errorResponse); + } + + const sessionId = await claudeService.continueClaudeCode(request); + + const response: SuccessResponse = { + success: true, + data: { session_id: sessionId }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'EXECUTION_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Resume existing Claude Code session + */ + router.post('/resume', async (req, res) => { + try { + const request = req.body as ResumeClaudeRequest; + + // Validate request + if (!request.project_path || !request.session_id || !request.prompt || !request.model) { + const errorResponse: ErrorResponse = { + error: 'Missing required fields: project_path, session_id, prompt, model', + code: 'VALIDATION_ERROR', + timestamp: new Date().toISOString(), + }; + return res.status(400).json(errorResponse); + } + + const sessionId = await claudeService.resumeClaudeCode(request); + + const response: SuccessResponse = { + success: true, + data: { session_id: sessionId }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'EXECUTION_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Cancel running Claude execution + */ + router.post('/cancel/:sessionId', async (req, res) => { + try { + const { sessionId } = req.params; + const cancelled = await claudeService.cancelClaudeExecution(sessionId); + + const response: SuccessResponse = { + success: true, + data: { cancelled, session_id: sessionId }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'CANCELLATION_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Get list of running Claude sessions + */ + router.get('/sessions/running', async (req, res) => { + try { + const sessions = claudeService.getRunningClaudeSessions(); + + const response: SuccessResponse = { + success: true, + data: sessions, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'SESSIONS_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Get session info by ID + */ + router.get('/sessions/:sessionId', async (req, res) => { + try { + const { sessionId } = req.params; + const sessionInfo = claudeService.getSessionInfo(sessionId); + + if (!sessionInfo) { + const errorResponse: ErrorResponse = { + error: 'Session not found', + code: 'SESSION_NOT_FOUND', + timestamp: new Date().toISOString(), + }; + return res.status(404).json(errorResponse); + } + + const response: SuccessResponse = { + success: true, + data: sessionInfo, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'SESSION_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Get session history/output + */ + router.get('/sessions/:sessionId/history', async (req, res) => { + try { + const { sessionId } = req.params; + const history = await projectService.loadSessionHistory(sessionId); + + const response: SuccessResponse = { + success: true, + data: { session_id: sessionId, history }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'HISTORY_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(404).json(errorResponse); + } + }); + + return router; +} \ No newline at end of file diff --git a/claudia-server/src/routes/projects.ts b/claudia-server/src/routes/projects.ts new file mode 100644 index 000000000..10fb4d848 --- /dev/null +++ b/claudia-server/src/routes/projects.ts @@ -0,0 +1,250 @@ +import { Router } from 'express'; +import type { ProjectService } from '../services/project.js'; +import type { SuccessResponse, ErrorResponse } from '../types/index.js'; + +/** + * Create an Express Router exposing project-related HTTP endpoints. + * + * The returned router is configured with routes for listing and creating projects, + * retrieving project sessions, finding and reading/saving `CLAUDE.md` files, and + * listing directory contents. Handlers delegate data operations to the provided + * ProjectService and respond with a consistent SuccessResponse or ErrorResponse + * shape that includes a timestamp. Validation failures return 400, missing + * resources return 404 where appropriate, and unexpected errors return 500 with + * route-specific error codes. + * + * @returns An Express Router wired with project management and CLAUDE file endpoints. + */ +export function createProjectRoutes(projectService: ProjectService): Router { + const router = Router(); + + /** + * List all projects + */ + router.get('/', async (req, res) => { + try { + const projects = await projectService.listProjects(); + + const response: SuccessResponse = { + success: true, + data: projects, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'PROJECTS_LIST_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Create a new project + */ + router.post('/', async (req, res) => { + try { + const { path } = req.body; + + if (!path) { + const errorResponse: ErrorResponse = { + error: 'Missing required field: path', + code: 'VALIDATION_ERROR', + timestamp: new Date().toISOString(), + }; + return res.status(400).json(errorResponse); + } + + const project = await projectService.createProject(path); + + const response: SuccessResponse = { + success: true, + data: project, + timestamp: new Date().toISOString(), + }; + + res.status(201).json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'PROJECT_CREATE_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Get sessions for a specific project + */ + router.get('/:projectId/sessions', async (req, res) => { + try { + const { projectId } = req.params; + const sessions = await projectService.getProjectSessions(projectId); + + const response: SuccessResponse = { + success: true, + data: sessions, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'PROJECT_SESSIONS_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Find CLAUDE.md files in a project + */ + router.get('/:projectId/claude-files', async (req, res) => { + try { + const { projectId } = req.params; + + // First get the project to find its actual path + const projects = await projectService.listProjects(); + const project = projects.find(p => p.id === projectId); + + if (!project) { + const errorResponse: ErrorResponse = { + error: 'Project not found', + code: 'PROJECT_NOT_FOUND', + timestamp: new Date().toISOString(), + }; + return res.status(404).json(errorResponse); + } + + const claudeFiles = await projectService.findClaudeMdFiles(project.path); + + const response: SuccessResponse = { + success: true, + data: claudeFiles, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'CLAUDE_FILES_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Read CLAUDE.md file content + */ + router.get('/claude-file', async (req, res) => { + try { + const { path } = req.query; + + if (!path || typeof path !== 'string') { + const errorResponse: ErrorResponse = { + error: 'Missing required query parameter: path', + code: 'VALIDATION_ERROR', + timestamp: new Date().toISOString(), + }; + return res.status(400).json(errorResponse); + } + + const content = await projectService.readClaudeMdFile(path); + + const response: SuccessResponse = { + success: true, + data: { path, content }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'CLAUDE_FILE_READ_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * Save CLAUDE.md file content + */ + router.put('/claude-file', async (req, res) => { + try { + const { path, content } = req.body; + + if (!path || !content) { + const errorResponse: ErrorResponse = { + error: 'Missing required fields: path, content', + code: 'VALIDATION_ERROR', + timestamp: new Date().toISOString(), + }; + return res.status(400).json(errorResponse); + } + + await projectService.saveClaudeMdFile(path, content); + + const response: SuccessResponse = { + success: true, + data: { path, saved: true }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'CLAUDE_FILE_SAVE_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + /** + * List directory contents + */ + router.get('/directory', async (req, res) => { + try { + const { path } = req.query; + + if (!path || typeof path !== 'string') { + const errorResponse: ErrorResponse = { + error: 'Missing required query parameter: path', + code: 'VALIDATION_ERROR', + timestamp: new Date().toISOString(), + }; + return res.status(400).json(errorResponse); + } + + const contents = await projectService.listDirectoryContents(path); + + const response: SuccessResponse = { + success: true, + data: { path, contents }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + } catch (error) { + const errorResponse: ErrorResponse = { + error: error instanceof Error ? error.message : 'Unknown error', + code: 'DIRECTORY_LIST_ERROR', + timestamp: new Date().toISOString(), + }; + res.status(500).json(errorResponse); + } + }); + + return router; +} \ No newline at end of file diff --git a/claudia-server/src/routes/status.ts b/claudia-server/src/routes/status.ts new file mode 100644 index 000000000..ce1b2a4f4 --- /dev/null +++ b/claudia-server/src/routes/status.ts @@ -0,0 +1,77 @@ +import { Router } from 'express'; +import type { SuccessResponse } from '../types/index.js'; +import { homedir } from 'os'; +import { join } from 'path'; + +/** + * Create an Express Router with status-related endpoints. + * + * Exposes three GET endpoints: + * - GET /health: returns runtime health data (status, uptime, memory usage, Node version) and a timestamp. + * - GET /info: returns server metadata (name, version, description) and runtime/environment details (node version, platform, architecture, pid, cwd, claude_home) with a timestamp. + * - GET /home: returns the current user's home directory and the server's Claude-specific directory path with a timestamp. + * + * @returns An Express Router configured with the above endpoints. + */ +export function createStatusRoutes(): Router { + const router = Router(); + + /** + * Health check endpoint + */ + router.get('/health', (req, res) => { + const response: SuccessResponse = { + success: true, + data: { + status: 'healthy', + uptime: process.uptime(), + memory: process.memoryUsage(), + version: process.version, + }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + }); + + /** + * Server info endpoint + */ + router.get('/info', (req, res) => { + const response: SuccessResponse = { + success: true, + data: { + name: 'Claudia Server', + version: '1.0.0', + description: 'Standalone TypeScript server for Claude Code integration', + node_version: process.version, + platform: process.platform, + architecture: process.arch, + pid: process.pid, + cwd: process.cwd(), + claude_home: join(homedir(), '.claude'), + }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + }); + + /** + * Get home directory + */ + router.get('/home', (req, res) => { + const response: SuccessResponse = { + success: true, + data: { + home_directory: homedir(), + claude_directory: join(homedir(), '.claude'), + }, + timestamp: new Date().toISOString(), + }; + + res.json(response); + }); + + return router; +} \ No newline at end of file diff --git a/claudia-server/src/server.ts b/claudia-server/src/server.ts new file mode 100644 index 000000000..ad3a78735 --- /dev/null +++ b/claudia-server/src/server.ts @@ -0,0 +1,257 @@ +import express from 'express'; +import cors from 'cors'; +import helmet from 'helmet'; +import compression from 'compression'; +import morgan from 'morgan'; +import { createServer } from 'http'; +import { ClaudeService } from './services/claude.js'; +import { ProjectService } from './services/project.js'; +import { WebSocketService } from './services/websocket.js'; +import { createClaudeRoutes } from './routes/claude.js'; +import { createProjectRoutes } from './routes/projects.js'; +import { createStatusRoutes } from './routes/status.js'; +import type { ServerConfig, ErrorResponse } from './types/index.js'; + +/** + * Main Claudia Server class + */ +export class ClaudiaServer { + private app: express.Application; + private server: any; + private config: ServerConfig; + private claudeService: ClaudeService; + private projectService: ProjectService; + private wsService: WebSocketService; + + constructor(config: Partial = {}) { + this.config = { + port: config.port || 3000, + host: config.host || '0.0.0.0', + cors_origin: config.cors_origin || ['http://localhost:3000'], + max_concurrent_sessions: config.max_concurrent_sessions || 10, + session_timeout_ms: config.session_timeout_ms || 300000, // 5 minutes + claude_binary_path: config.claude_binary_path, + claude_home_dir: config.claude_home_dir, + }; + + this.app = express(); + this.server = createServer(this.app); + + // Initialize services + this.claudeService = new ClaudeService(this.config.claude_binary_path); + this.projectService = new ProjectService(this.config.claude_home_dir); + this.wsService = new WebSocketService(this.server); + + this.setupMiddleware(); + this.setupRoutes(); + this.setupWebSocketEvents(); + this.setupErrorHandling(); + } + + private setupMiddleware(): void { + // Security middleware + this.app.use(helmet({ + contentSecurityPolicy: false, // Disable CSP for API server + })); + + // CORS + this.app.use(cors({ + origin: this.config.cors_origin, + credentials: true, + })); + + // Compression + this.app.use(compression()); + + // Logging + this.app.use(morgan('combined')); + + // Body parsing + this.app.use(express.json({ limit: '10mb' })); + this.app.use(express.urlencoded({ extended: true, limit: '10mb' })); + + // Request timeout + this.app.use((req, res, next) => { + req.setTimeout(30000); // 30 seconds + next(); + }); + } + + private setupRoutes(): void { + // API routes + this.app.use('/api/claude', createClaudeRoutes(this.claudeService, this.projectService)); + this.app.use('/api/projects', createProjectRoutes(this.projectService)); + this.app.use('/api/status', createStatusRoutes()); + + // Root endpoint + this.app.get('/', (req, res) => { + res.json({ + name: 'Claudia Server', + version: '1.0.0', + description: 'Standalone TypeScript server for Claude Code integration', + endpoints: { + api: '/api', + websocket: '/ws', + health: '/api/status/health', + info: '/api/status/info', + }, + timestamp: new Date().toISOString(), + }); + }); + + // 404 handler + this.app.use('*', (req, res) => { + const errorResponse: ErrorResponse = { + error: 'Not Found', + code: 'NOT_FOUND', + timestamp: new Date().toISOString(), + details: { path: req.originalUrl, method: req.method }, + }; + res.status(404).json(errorResponse); + }); + } + + private setupWebSocketEvents(): void { + // Forward Claude service events to WebSocket clients + this.claudeService.on('claude_stream', (data) => { + this.wsService.broadcastClaudeStream(data.session_id, data.message); + }); + + this.claudeService.on('claude_output', (data) => { + this.wsService.broadcastClaudeStream(data.session_id, { + type: 'output', + content: data.data, + timestamp: new Date().toISOString(), + }); + }); + + this.claudeService.on('claude_error', (data) => { + this.wsService.broadcastClaudeStream(data.session_id, { + type: 'error', + content: data.error, + timestamp: new Date().toISOString(), + }); + }); + + this.claudeService.on('claude_exit', (data) => { + this.wsService.broadcastClaudeStream(data.session_id, { + type: 'complete', + content: `Process exited with code ${data.code}`, + timestamp: new Date().toISOString(), + }); + }); + } + + private setupErrorHandling(): void { + // Global error handler + this.app.use((error: Error, req: express.Request, res: express.Response, next: express.NextFunction) => { + console.error('Unhandled error:', error); + + const errorResponse: ErrorResponse = { + error: 'Internal Server Error', + code: 'INTERNAL_ERROR', + timestamp: new Date().toISOString(), + details: process.env.NODE_ENV === 'development' ? { + message: error.message, + stack: error.stack, + } : undefined, + }; + + res.status(500).json(errorResponse); + }); + + // Handle uncaught exceptions + process.on('uncaughtException', (error) => { + console.error('Uncaught Exception:', error); + this.gracefulShutdown('UNCAUGHT_EXCEPTION'); + }); + + // Handle unhandled rejections + process.on('unhandledRejection', (reason, promise) => { + console.error('Unhandled Rejection at:', promise, 'reason:', reason); + this.gracefulShutdown('UNHANDLED_REJECTION'); + }); + + // Handle shutdown signals + process.on('SIGTERM', () => { + console.log('Received SIGTERM, shutting down gracefully'); + this.gracefulShutdown('SIGTERM'); + }); + + process.on('SIGINT', () => { + console.log('Received SIGINT, shutting down gracefully'); + this.gracefulShutdown('SIGINT'); + }); + } + + /** + * Start the server + */ + async start(): Promise { + return new Promise((resolve, reject) => { + this.server.listen(this.config.port, this.config.host, (error?: Error) => { + if (error) { + reject(error); + } else { + console.log(`🚀 Claudia Server started on http://${this.config.host}:${this.config.port}`); + console.log(`📡 WebSocket endpoint: ws://${this.config.host}:${this.config.port}/ws`); + console.log(`🏠 Claude home directory: ${this.claudeService.getClaudeHomeDir()}`); + resolve(); + } + }); + }); + } + + /** + * Stop the server gracefully + */ + async stop(): Promise { + return new Promise((resolve) => { + console.log('Stopping Claudia Server...'); + + // Cleanup services + this.claudeService.cleanup(); + this.wsService.close(); + + // Close server + this.server.close(() => { + console.log('Claudia Server stopped'); + resolve(); + }); + }); + } + + /** + * Graceful shutdown handler + */ + private gracefulShutdown(signal: string): void { + console.log(`Shutting down gracefully (${signal})...`); + + this.stop().then(() => { + process.exit(0); + }).catch((error) => { + console.error('Error during shutdown:', error); + process.exit(1); + }); + } + + /** + * Get server configuration + */ + getConfig(): ServerConfig { + return { ...this.config }; + } + + /** + * Get server status + */ + getStatus() { + return { + uptime: process.uptime(), + memory: process.memoryUsage(), + connections: this.wsService.getConnectedClientsCount(), + subscriptions: this.wsService.getActiveSubscriptions(), + running_sessions: this.claudeService.getRunningClaudeSessions().length, + }; + } +} \ No newline at end of file diff --git a/claudia-server/src/services/__tests__/claude.service.spec.ts b/claudia-server/src/services/__tests__/claude.service.spec.ts new file mode 100644 index 000000000..4c71d3860 --- /dev/null +++ b/claudia-server/src/services/__tests__/claude.service.spec.ts @@ -0,0 +1,505 @@ +import { EventEmitter } from 'events'; + +// We mock child_process.spawn, fs.promises.access, and os.homedir to ensure deterministic behavior +import * as childProcess from 'child_process'; +import { promises as fs } from 'fs'; +import * as os from 'os'; +import * as path from 'path'; + +// Import the class under test +// The provided file is named `claude.test.ts` but contains the service implementation. +// We import it directly as that's where the ClaudeService is exported. +import { ClaudeService } from '../claude.test'; + +// Test helpers to craft controllable fake ChildProcess instances +class FakeChildProcess extends EventEmitter { + public pid: number | undefined; + public stdout?: EventEmitter; + public stderr?: EventEmitter; + public killed = false; + + constructor(withPid = true) { + super(); + this.pid = withPid ? Math.floor(Math.random() * 10000) + 1000 : undefined; + this.stdout = new EventEmitter(); + this.stderr = new EventEmitter(); + } + + kill = (signal?: NodeJS.Signals | number) => { + this.killed = true; + // No-op; tests will assert on flags or trigger "close" as needed + return true; + }; +} + +// Jest-compatible mocks (works in Vitest with vi.mock as well) +jest.mock('child_process', () => { + const actual = jest.requireActual('child_process'); + return { + ...actual, + spawn: jest.fn(), + }; +}); + +jest.mock('fs', () => { + const actual = jest.requireActual('fs'); + return { + ...actual, + promises: { + ...(actual.promises || {}), + access: jest.fn(), + }, + }; +}); + +jest.mock('os', () => { + const actual = jest.requireActual('os'); + return { + ...actual, + homedir: jest.fn(), + }; +}); + +describe('ClaudeService', () => { + const mockedSpawn = childProcess.spawn as unknown as jest.Mock; + const mockedFsAccess = fs.access as unknown as jest.Mock; + const mockedHomedir = os.homedir as unknown as jest.Mock; + + const CLAUDE_BIN = '/fake/path/claude'; + + beforeEach(() => { + jest.useFakeTimers(); + jest.spyOn(global, 'setTimeout'); + + mockedHomedir.mockReturnValue('/home/testuser'); + mockedFsAccess.mockResolvedValue(undefined); + mockedSpawn.mockReset(); + }); + + afterEach(() => { + jest.useRealTimers(); + jest.clearAllMocks(); + }); + + function setupSpawnForVersion(output: string, exitCode: number) { + const fake = new FakeChildProcess(true); + // For --version call invoked by findClaudeBinary().testClaudeBinary() and by runCommand + mockedSpawn.mockImplementation((_cmd: string, args: string[]) => { + // args may include --version or other CLI flags + // simulate emission of output to stdout + setImmediate(() => { + if (output) { + fake.stdout?.emit('data', Buffer.from(output)); + } + // stderr is unused for success path; keep empty + fake.emit('close', exitCode); + }); + return fake as unknown as childProcess.ChildProcess; + }); + return fake; + } + + function setupSpawnForRunCommand(stdoutOutput: string, stderrOutput: string, exitCode: number) { + const fake = new FakeChildProcess(true); + mockedSpawn.mockImplementation((_cmd: string, _args: string[]) => { + setImmediate(() => { + if (stdoutOutput) fake.stdout?.emit('data', Buffer.from(stdoutOutput)); + if (stderrOutput) fake.stderr?.emit('data', Buffer.from(stderrOutput)); + fake.emit('close', exitCode); + }); + return fake as unknown as childProcess.ChildProcess; + }); + return fake; + } + + function setupSpawnForStreaming(argsMatcher: (args: string[]) => boolean, events: Array<{ type: 'stdout' | 'stderr' | 'close' | 'error'; data?: string; code?: number; error?: Error }>) { + const fake = new FakeChildProcess(true); + mockedSpawn.mockImplementation((_cmd: string, args: string[]) => { + // Only stream for the matching command invocation; other spawns (e.g. --version) need separate stubs in tests + if (argsMatcher(args)) { + setImmediate(() => { + for (const ev of events) { + if (ev.type === 'stdout' && ev.data !== undefined) { + fake.stdout?.emit('data', Buffer.from(ev.data)); + } else if (ev.type === 'stderr' && ev.data !== undefined) { + fake.stderr?.emit('data', Buffer.from(ev.data)); + } else if (ev.type === 'close') { + fake.emit('close', ev.code ?? 0); + } else if (ev.type === 'error') { + fake.emit('error', ev.error ?? new Error('spawn error')); + } + } + }); + } + return fake as unknown as childProcess.ChildProcess; + }); + return fake; + } + + describe('checkClaudeVersion', () => { + it('returns installed=true with parsed semver when binary is valid and outputs version', async () => { + // findClaudeBinary -> testClaudeBinary uses spawn(..., ['--version']) + setupSpawnForVersion('claude version 1.2.3\n', 0); + + // runCommand(..., ['--version']) is also used; provide output there too + setupSpawnForRunCommand('claude version 1.2.3\n', '', 0); + + const svc = new ClaudeService(CLAUDE_BIN); + const res = await svc.checkClaudeVersion(); + + expect(res.is_installed).toBe(true); + expect(res.version).toBe('1.2.3'); + expect(res.output).toContain('claude'); + }); + + it('returns installed=true but undefined version if format lacks semver', async () => { + setupSpawnForVersion('claude build main\n', 0); + setupSpawnForRunCommand('claude build main\n', '', 0); + + const svc = new ClaudeService(CLAUDE_BIN); + const res = await svc.checkClaudeVersion(); + + expect(res.is_installed).toBe(true); + expect(res.version).toBeUndefined(); + expect(res.output).toContain('claude'); + }); + + it('returns installed=false when binary missing or invalid', async () => { + // fs.access ok, but testClaudeBinary fails via non-zero exit or non-matching output + setupSpawnForVersion('something else\n', 1); + + const svc = new ClaudeService(CLAUDE_BIN); + const res = await svc.checkClaudeVersion(); + + expect(res.is_installed).toBe(false); + expect(res.output).toMatch(/Invalid Claude binary|not found|Unknown error/i); + }); + }); + + describe('execute/continue/resume stream handling and registry', () => { + it('executeClaudeCode: streams JSON and non-JSON lines, emits events, and registers process', async () => { + // testClaudeBinary path when finding claude binary + setupSpawnForVersion('claude 1.0.0', 0); + + const json1 = JSON.stringify({ type: 'token', text: 'Hello' }); + const json2 = JSON.stringify({ type: 'done' }); + + // stream-json invocation args matcher + const isExecuteArgs = (args: string[]) => + args.includes('-p') && + args.includes('--model') && + args.includes('--output-format') && + args.includes('stream-json') && + !args.includes('-c') && + !args.includes('--resume'); + + // streaming events: stdout lines include JSON and a non-JSON line + const streamEvents = [ + { type: 'stdout' as const, data: json1 + '\n' }, + { type: 'stdout' as const, data: 'raw text line\n' }, + { type: 'stdout' as const, data: json2 + '\n' }, + { type: 'stderr' as const, data: 'warning: something\n' }, + { type: 'close' as const, code: 0 }, + ]; + + setupSpawnForStreaming(isExecuteArgs, streamEvents); + + const svc = new ClaudeService(CLAUDE_BIN); + + const streamEventsCaptured: any[] = []; + const outputEventsCaptured: any[] = []; + const errorEventsCaptured: any[] = []; + const exitEventsCaptured: any[] = []; + + svc.on('claude_stream', (e) => streamEventsCaptured.push(e)); + svc.on('claude_output', (e) => outputEventsCaptured.push(e)); + svc.on('claude_error', (e) => errorEventsCaptured.push(e)); + svc.on('claude_exit', (e) => exitEventsCaptured.push(e)); + + const sessionId = await svc.executeClaudeCode({ + prompt: 'Do something', + model: 'claude-3', + project_path: '/tmp/project', + }); + + expect(typeof sessionId).toBe('string'); + // wait microtasks to flush stream events + await Promise.resolve(); + + // Validate stream parsing and event enrichment with session_id/timestamp + expect(streamEventsCaptured.length).toBe(2); + for (const ev of streamEventsCaptured) { + expect(ev.session_id).toBe(sessionId); + expect(ev.message).toHaveProperty('timestamp'); + expect(ev.message).toHaveProperty('session_id', sessionId); + } + + expect(outputEventsCaptured.length).toBe(1); + expect(outputEventsCaptured[0].session_id).toBe(sessionId); + expect(outputEventsCaptured[0].data).toBe('raw text line'); + + expect(errorEventsCaptured.length).toBe(1); + expect(errorEventsCaptured[0].session_id).toBe(sessionId); + expect(errorEventsCaptured[0].error).toContain('warning'); + + // exit event fired and registries cleared + // wait for close handling + await Promise.resolve(); + expect(exitEventsCaptured.length).toBe(1); + expect(exitEventsCaptured[0].session_id).toBe(sessionId); + expect(typeof exitEventsCaptured[0].code).toBe('number'); + + // After exit, process should be removed from registries + expect(svc.getSessionInfo(sessionId)).toBeUndefined(); + expect(svc.getRunningClaudeSessions().find(s => (s as any).process_type?.ClaudeSession?.session_id === sessionId)).toBeUndefined(); + }); + + it('continueClaudeCode: uses -c flag and registers until exit', async () => { + setupSpawnForVersion('claude 1.0.0', 0); + + const isContinueArgs = (args: string[]) => args.includes('-c'); + setupSpawnForStreaming(isContinueArgs, [ + { type: 'stdout', data: JSON.stringify({ type: 'token', text: 'cont' }) + '\n' }, + { type: 'close', code: 0 }, + ]); + + const svc = new ClaudeService(CLAUDE_BIN); + const exitEvents: any[] = []; + svc.on('claude_exit', (e) => exitEvents.push(e)); + + const sessionId = await svc.continueClaudeCode({ + prompt: 'Continue', + model: 'claude-3', + project_path: '/tmp/project', + }); + + expect(typeof sessionId).toBe('string'); + await Promise.resolve(); + expect(svc.getSessionInfo(sessionId)).toBeUndefined(); + expect(exitEvents.length).toBe(1); + }); + + it('resumeClaudeCode: uses --resume and given session_id', async () => { + setupSpawnForVersion('claude 1.0.0', 0); + + const isResumeArgs = (args: string[]) => args.includes('--resume') && args.some((a) => a === 'existing-session'); + setupSpawnForStreaming(isResumeArgs, [ + { type: 'stdout', data: JSON.stringify({ type: 'token', text: 'resumed' }) + '\n' }, + { type: 'close', code: 0 }, + ]); + + const svc = new ClaudeService(CLAUDE_BIN); + const exitEvents: any[] = []; + svc.on('claude_exit', (e) => exitEvents.push(e)); + + const sessionId = await svc.resumeClaudeCode({ + prompt: 'Resume please', + model: 'claude-3', + project_path: '/tmp/project', + session_id: 'existing-session', + }); + + expect(sessionId).toBe('existing-session'); + await Promise.resolve(); + expect(exitEvents.length).toBe(1); + }); + + it('emits claude_error and clears registries on spawn error', async () => { + setupSpawnForVersion('claude 1.0.0', 0); + + const isExecuteArgs = (args: string[]) => args.includes('-p') && args.includes('Fail'); + const fake = setupSpawnForStreaming(isExecuteArgs, [ + { type: 'error', error: new Error('spawn failure') }, + ]); + + const svc = new ClaudeService(CLAUDE_BIN); + const errors: any[] = []; + svc.on('claude_error', (e) => errors.push(e)); + + const sessionId = await svc.executeClaudeCode({ + prompt: 'Fail', + model: 'claude-3', + project_path: '/tmp/project', + }); + + // Ensure error was emitted with session_id and message + await Promise.resolve(); + expect(errors.length).toBe(1); + expect(errors[0].session_id).toBe(sessionId); + expect(errors[0].error).toContain('spawn failure'); + + // Make sure registries are cleared on error + expect(svc.getSessionInfo(sessionId)).toBeUndefined(); + expect(svc.getRunningClaudeSessions().find(s => (s as any).process_type?.ClaudeSession?.session_id === sessionId)).toBeUndefined(); + + // Avoid eslint 'unused' warning for fake + expect(fake).toBeTruthy(); + }); + }); + + describe('cancelClaudeExecution', () => { + it('returns true and sends SIGTERM; SIGKILL after 5s if not killed', async () => { + setupSpawnForVersion('claude 1.0.0', 0); + + // The process that will be stored/registered; we need it to not exit immediately + const isExecuteArgs = (args: string[]) => args.includes('-p') && args.includes('cancel test'); + const fake = new FakeChildProcess(true); + (childProcess.spawn as jest.Mock).mockImplementation((_cmd: string, args: string[]) => { + if (isExecuteArgs(args)) { + // keep it alive; do not emit close immediately + return fake as unknown as childProcess.ChildProcess; + } + // For binary validation + const ver = new FakeChildProcess(true); + setImmediate(() => ver.emit('close', 0)); + return ver as unknown as childProcess.ChildProcess; + }); + + const svc = new ClaudeService(CLAUDE_BIN); + const id = await svc.executeClaudeCode({ + prompt: 'cancel test', + model: 'claude-3', + project_path: '/tmp/project', + }); + + const result = await svc.cancelClaudeExecution(id); + expect(result).toBe(true); + + // Force the delayed SIGKILL path by leaving fake.killed=false + jest.advanceTimersByTime(5000); + expect(setTimeout).toHaveBeenCalled(); + // We can only assert that kill was invoked; our FakeChildProcess.kill toggles .killed=true on any call + expect(fake.killed).toBe(true); + }); + + it('returns false when no such session exists', async () => { + const svc = new ClaudeService(CLAUDE_BIN); + const res = await svc.cancelClaudeExecution('non-existent'); + expect(res).toBe(false); + }); + }); + + describe('registry and helpers', () => { + it('getRunningClaudeSessions and getSessionInfo reflect active process; cleared on close', async () => { + setupSpawnForVersion('claude 1.0.0', 0); + + const isExecuteArgs = (args: string[]) => args.includes('-p') && args.includes('track me'); + const events = [ + { type: 'stdout' as const, data: JSON.stringify({ type: 'token', text: 'hi' }) + '\n' }, + { type: 'close' as const, code: 0 }, + ]; + setupSpawnForStreaming(isExecuteArgs, events); + + const svc = new ClaudeService(CLAUDE_BIN); + const id = await svc.executeClaudeCode({ + prompt: 'track me', + model: 'claude-3', + project_path: '/tmp/prj', + }); + + // Immediately after spawn, registry should have the session + const running = svc.getRunningClaudeSessions(); + expect(running.length).toBe(1); + const info = svc.getSessionInfo(id); + expect(info).toBeDefined(); + expect(info?.model).toBe('claude-3'); + expect(info?.project_path).toBe('/tmp/prj'); + expect(info?.process_type).toEqual({ ClaudeSession: { session_id: id } }); + + // After close, cleared + await Promise.resolve(); + expect(svc.getSessionInfo(id)).toBeUndefined(); + expect(svc.getRunningClaudeSessions().length).toBe(0); + }); + + it('getClaudeHomeDir returns ~/.claude', () => { + mockedHomedir.mockReturnValue('/home/userx'); + const svc = new ClaudeService(CLAUDE_BIN); + expect(svc.getClaudeHomeDir()).toBe(path.join('/home/userx', '.claude')); + }); + + it('cleanup kills all tracked processes and clears registries', async () => { + setupSpawnForVersion('claude 1.0.0', 0); + + // Keep the process running so cleanup has something to kill + const isExecuteArgs = (args: string[]) => args.includes('-p') && args.includes('cleanup'); + const fake = new FakeChildProcess(true); + mockedSpawn.mockImplementation((_cmd: string, args: string[]) => { + if (isExecuteArgs(args)) { + return fake as unknown as childProcess.ChildProcess; + } + const ver = new FakeChildProcess(true); + setImmediate(() => ver.emit('close', 0)); + return ver as unknown as childProcess.ChildProcess; + }); + + const svc = new ClaudeService(CLAUDE_BIN); + const id = await svc.executeClaudeCode({ + prompt: 'cleanup', + model: 'claude-3', + project_path: '/tmp/p', + }); + + expect(svc.getSessionInfo(id)).toBeDefined(); + + // Invoke cleanup + svc.cleanup(); + + // Process array cleared + expect(svc.getSessionInfo(id)).toBeUndefined(); + expect(svc.getRunningClaudeSessions().length).toBe(0); + // Fake process kill called + expect(fake.killed).toBe(true); + }); + }); + + describe('runCommand failure path (indirectly via checkClaudeVersion)', () => { + it('bubbles up non-zero exit errors from runCommand', async () => { + // testClaudeBinary ok + setupSpawnForVersion('claude 1.0.0', 0); + + // Next spawn for runCommand returns non-zero with stderr message + mockedSpawn.mockImplementationOnce((_cmd: string, _args: string[]) => { + // This first call already consumed by setupSpawnForVersion. We need second for runCommand: + // To ensure we target runCommand, we return a process that emits stderr and close 2. + const fake = new FakeChildProcess(true); + setImmediate(() => { + fake.stderr?.emit('data', Buffer.from('boom')); + fake.emit('close', 2); + }); + return fake as unknown as childProcess.ChildProcess; + }); + + const svc = new ClaudeService(CLAUDE_BIN); + const res = await svc.checkClaudeVersion(); + expect(res.is_installed).toBe(false); + expect(res.output).toContain('boom'); + }); + }); + + describe('spawn failure to start (no pid)', () => { + it('throws if spawn returns process without pid', async () => { + setupSpawnForVersion('claude 1.0.0', 0); + + // Create a child process without pid to trigger the guard + const badChild = new FakeChildProcess(false); + mockedSpawn.mockImplementation((_cmd: string, args: string[]) => { + // for the execution spawn (not version check), return bad child + if (args.includes('--output-format')) { + return badChild as unknown as childProcess.ChildProcess; + } + // for version checks, return normal that closes immediately + const ver = new FakeChildProcess(true); + setImmediate(() => ver.emit('close', 0)); + return ver as unknown as childProcess.ChildProcess; + }); + + const svc = new ClaudeService(CLAUDE_BIN); + await expect(svc.executeClaudeCode({ + prompt: 'start please', + model: 'claude-3', + project_path: '/tmp/pp', + })).rejects.toThrow(/Failed to start Claude process/); + }); + }); +}); \ No newline at end of file diff --git a/claudia-server/src/services/claude.ts b/claudia-server/src/services/claude.ts new file mode 100644 index 000000000..292808ed9 --- /dev/null +++ b/claudia-server/src/services/claude.ts @@ -0,0 +1,352 @@ +import { spawn, ChildProcess } from 'child_process'; +import { EventEmitter } from 'events'; +import { v4 as uuidv4 } from 'uuid'; +import { promises as fs } from 'fs'; +import { join, dirname } from 'path'; +import { homedir } from 'os'; +import type { + ClaudeStreamMessage, + ProcessInfo, + ClaudeVersionStatus, + ExecuteClaudeRequest, + ContinueClaudeRequest, + ResumeClaudeRequest, +} from '../types/index.js'; + +/** + * Service for managing Claude Code CLI processes + */ +export class ClaudeService extends EventEmitter { + private processes: Map = new Map(); + private processRegistry: Map = new Map(); + + constructor(private claudeBinaryPath?: string) { + super(); + } + + /** + * Find Claude binary in common locations + */ + private async findClaudeBinary(): Promise { + if (this.claudeBinaryPath) { + try { + await fs.access(this.claudeBinaryPath); + return this.claudeBinaryPath; + } catch { + // Fall through to search + } + } + + const searchPaths = [ + 'claude', // In PATH + '/usr/local/bin/claude', + '/opt/homebrew/bin/claude', + join(homedir(), '.local/bin/claude'), + join(homedir(), '.nvm/versions/node/*/bin/claude'), + ]; + + for (const path of searchPaths) { + try { + // Test if binary exists and is executable + await this.testClaudeBinary(path); + return path; + } catch { + continue; + } + } + + throw new Error('Claude binary not found. Please install Claude Code CLI.'); + } + + /** + * Test if a Claude binary path is valid + */ + private async testClaudeBinary(path: string): Promise { + return new Promise((resolve, reject) => { + const child = spawn(path, ['--version'], { stdio: 'pipe' }); + let output = ''; + + child.stdout?.on('data', (data) => { + output += data.toString(); + }); + + child.stderr?.on('data', (data) => { + output += data.toString(); + }); + + child.on('close', (code) => { + if (code === 0 && output.includes('claude')) { + resolve(); + } else { + reject(new Error(`Invalid Claude binary: ${path}`)); + } + }); + + child.on('error', reject); + }); + } + + /** + * Get Claude version and installation status + */ + async checkClaudeVersion(): Promise { + try { + const claudePath = await this.findClaudeBinary(); + const output = await this.runCommand(claudePath, ['--version']); + + const versionMatch = output.match(/claude[^\d]*(\d+\.\d+\.\d+)/i); + const version = versionMatch ? versionMatch[1] : undefined; + + return { + is_installed: true, + version, + output: output.trim(), + }; + } catch (error) { + return { + is_installed: false, + output: error instanceof Error ? error.message : 'Unknown error', + }; + } + } + + /** + * Execute a command and return output + */ + private async runCommand(command: string, args: string[], cwd?: string): Promise { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { cwd, stdio: 'pipe' }); + let output = ''; + let error = ''; + + child.stdout?.on('data', (data) => { + output += data.toString(); + }); + + child.stderr?.on('data', (data) => { + error += data.toString(); + }); + + child.on('close', (code) => { + if (code === 0) { + resolve(output); + } else { + reject(new Error(error || `Command failed with code ${code}`)); + } + }); + + child.on('error', reject); + }); + } + + /** + * Execute Claude Code with streaming output + */ + async executeClaudeCode(request: ExecuteClaudeRequest): Promise { + const sessionId = uuidv4(); + const claudePath = await this.findClaudeBinary(); + + const args = [ + '-p', + request.prompt, + '--model', + request.model, + '--output-format', + 'stream-json', + '--verbose', + '--dangerously-skip-permissions', + ]; + + await this.spawnClaudeProcess(sessionId, claudePath, args, request.project_path, request); + return sessionId; + } + + /** + * Continue existing Claude Code conversation + */ + async continueClaudeCode(request: ContinueClaudeRequest): Promise { + const sessionId = uuidv4(); + const claudePath = await this.findClaudeBinary(); + + const args = [ + '-c', // Continue flag + '-p', + request.prompt, + '--model', + request.model, + '--output-format', + 'stream-json', + '--verbose', + '--dangerously-skip-permissions', + ]; + + await this.spawnClaudeProcess(sessionId, claudePath, args, request.project_path, request); + return sessionId; + } + + /** + * Resume existing Claude Code session + */ + async resumeClaudeCode(request: ResumeClaudeRequest): Promise { + const sessionId = request.session_id; + const claudePath = await this.findClaudeBinary(); + + const args = [ + '--resume', + request.session_id, + '-p', + request.prompt, + '--model', + request.model, + '--output-format', + 'stream-json', + '--verbose', + '--dangerously-skip-permissions', + ]; + + await this.spawnClaudeProcess(sessionId, claudePath, args, request.project_path, request); + return sessionId; + } + + /** + * Spawn Claude process with streaming output + */ + private async spawnClaudeProcess( + sessionId: string, + claudePath: string, + args: string[], + projectPath: string, + request: any + ): Promise { + const child = spawn(claudePath, args, { + cwd: projectPath, + stdio: 'pipe', + env: { ...process.env }, + }); + + if (!child.pid) { + throw new Error('Failed to start Claude process'); + } + + // Register process + const processInfo: ProcessInfo = { + run_id: Date.now(), + process_type: { ClaudeSession: { session_id: sessionId } }, + pid: child.pid, + started_at: new Date().toISOString(), + project_path: projectPath, + task: request.prompt.substring(0, 100), + model: request.model, + }; + + this.processes.set(sessionId, child); + this.processRegistry.set(sessionId, processInfo); + + // Handle stdout (streaming JSON) + child.stdout?.on('data', (data) => { + const lines = data.toString().split('\n').filter((line: string) => line.trim()); + + for (const line of lines) { + try { + const message = JSON.parse(line) as ClaudeStreamMessage; + message.session_id = sessionId; + message.timestamp = new Date().toISOString(); + + this.emit('claude_stream', { + session_id: sessionId, + message, + }); + } catch (error) { + // Non-JSON line, emit as raw output + this.emit('claude_output', { + session_id: sessionId, + data: line, + }); + } + } + }); + + // Handle stderr + child.stderr?.on('data', (data) => { + this.emit('claude_error', { + session_id: sessionId, + error: data.toString(), + }); + }); + + // Handle process exit + child.on('close', (code) => { + this.processes.delete(sessionId); + this.processRegistry.delete(sessionId); + + this.emit('claude_exit', { + session_id: sessionId, + code, + }); + }); + + child.on('error', (error) => { + this.processes.delete(sessionId); + this.processRegistry.delete(sessionId); + + this.emit('claude_error', { + session_id: sessionId, + error: error.message, + }); + }); + } + + /** + * Cancel a running Claude process + */ + async cancelClaudeExecution(sessionId: string): Promise { + const process = this.processes.get(sessionId); + + if (process) { + process.kill('SIGTERM'); + + // Force kill after 5 seconds if not terminated + setTimeout(() => { + if (!process.killed) { + process.kill('SIGKILL'); + } + }, 5000); + + return true; + } + + return false; + } + + /** + * Get list of running Claude sessions + */ + getRunningClaudeSessions(): ProcessInfo[] { + return Array.from(this.processRegistry.values()); + } + + /** + * Get session info by ID + */ + getSessionInfo(sessionId: string): ProcessInfo | undefined { + return this.processRegistry.get(sessionId); + } + + /** + * Get Claude home directory (~/.claude) + */ + getClaudeHomeDir(): string { + return join(homedir(), '.claude'); + } + + /** + * Cleanup all processes + */ + cleanup(): void { + for (const [sessionId, process] of this.processes) { + process.kill('SIGTERM'); + } + this.processes.clear(); + this.processRegistry.clear(); + } +} \ No newline at end of file diff --git a/claudia-server/src/services/project.test.ts b/claudia-server/src/services/project.test.ts new file mode 100644 index 000000000..c8f478a0c --- /dev/null +++ b/claudia-server/src/services/project.test.ts @@ -0,0 +1,614 @@ +/* + Tests for ProjectService + + Testing framework and runner: Jest (TypeScript) with ts-jest assumed based on typical setup. + If this repository uses Vitest instead, replace jest.mock/jest.fn with vi.mock/vi.fn and update imports accordingly. + + Focus: Validate behaviors across happy paths, edge cases, and failure conditions for: + - listProjects + - createProject + - getProjectSessions + - loadSessionHistory + - findClaudeMdFiles + findClaudeMdFilesRecursive + - readClaudeMdFile + - saveClaudeMdFile + - listDirectoryContents + + Strategy: + - Mock fs.promises: access, readdir, stat, readFile, writeFile, mkdir + - Use a custom claudeHomeDir so we can assert generated paths deterministically + - Avoid real filesystem IO +*/ + +import { join, basename } from 'path'; + +// Import the service under test. Adjust the import path if the actual service file differs. +// The provided snippet indicates this class is exported as 'ProjectService' from claudia-server/src/services/project.ts +// If the implementation resides elsewhere, update this import accordingly. +import { ProjectService } from './project'; // <-- if this path differs in repo, update accordingly + +// Types used in assertions (optional). If types module path differs, you can remove the import and assert using runtime shapes. +type Project = { + id: string; + path: string; + sessions: string[]; + created_at: number; + most_recent_session?: number; +}; + +type Session = { + id: string; + project_id: string; + project_path: string; + created_at: number; + first_message?: string; + message_timestamp?: string; + todo_data?: any; +}; + +// We will mock fs.promises interface only. +type DirentLike = { + name: string; + isFile: () => boolean; + isDirectory: () => boolean; +}; + +// Build a manual mock for fs to control fs.promises methods precisely. +jest.mock('fs', () => { + const original = jest.requireActual('fs'); + const mem: Record = {}; + + // Not using memfs dependency; we'll stub readFile/writeFile via handlers configured per test + + return { + ...original, + promises: { + access: jest.fn(), + readdir: jest.fn(), + stat: jest.fn(), + readFile: jest.fn(), + writeFile: jest.fn(), + mkdir: jest.fn(), + }, + }; +}); + +const fsPromises = (require('fs').promises) as { + access: jest.Mock; + readdir: jest.Mock; + stat: jest.Mock; + readFile: jest.Mock; + writeFile: jest.Mock; + mkdir: jest.Mock; +}; + +// Helper to create a Dirent-like object +function dirent(name: string, kind: 'file' | 'dir'): DirentLike { + return { + name, + isFile: () => kind === 'file', + isDirectory: () => kind === 'dir', + }; +} + +// Common test variables +const HOME = '/tmp/claude-home'; +const PROJECTS_DIR = join(HOME, 'projects'); +const TODOS_DIR = join(HOME, 'todos'); + +// Date helpers +function fakeDate(epochSecs: number) { + return new Date(epochSecs * 1000); +} + +function statWithTimes({ ctime, mtime, birthtime } : { ctime: number; mtime?: number; birthtime?: number }) { + return { + ctime: fakeDate(ctime), + mtime: fakeDate(mtime ?? ctime), + birthtime: birthtime ? fakeDate(birthtime) : fakeDate(0), + }; +} + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('ProjectService', () => { + describe('listProjects', () => { + it('returns empty array if projects directory does not exist', async () => { + const svc = new ProjectService(HOME); + fsPromises.access.mockRejectedValueOnce(Object.assign(new Error('no dir'), { code: 'ENOENT' })); + + const projects = await svc.listProjects(); + + expect(projects).toEqual([]); + expect(fsPromises.access).toHaveBeenCalledWith(PROJECTS_DIR); + }); + + it('lists projects, reading sessions and sorting by most recent session then creation time', async () => { + const svc = new ProjectService(HOME); + // access ok + fsPromises.access.mockResolvedValueOnce(undefined); + + // Projects directory contains two projects: a-foo-bar and z-bar + fsPromises.readdir.mockResolvedValueOnce([ + dirent('a-foo-bar', 'dir'), + dirent('z-bar', 'dir'), + dirent('ignore.txt', 'file'), + ]); + + // For getProjectFromDirectory('a-foo-bar'): + fsPromises.stat + // stat(projectDir) + .mockResolvedValueOnce(statWithTimes({ ctime: 1000 })) + // stat(session1) for a-foo-bar + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 2000 })) + // stat(session2) for a-foo-bar + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 1500 })) + + // For getProjectFromDirectory('z-bar'): + // stat(projectDir) + .mockResolvedValueOnce(statWithTimes({ ctime: 900 })) + // stat(session for z-bar) + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 1200 })); + + // readdir(projectDir) calls inside getProjectFromDirectory: + fsPromises.readdir + // entries in a-foo-bar + .mockResolvedValueOnce([ + dirent('123.jsonl', 'file'), + dirent('456.jsonl', 'file'), + dirent('ignore.md', 'file'), + ]) + // entries in z-bar + .mockResolvedValueOnce([ + dirent('789.jsonl', 'file'), + ]); + + // getProjectPathFromSessions logic: will read session files and pick first with cwd + // For a-foo-bar: read 123.jsonl -> has cwd, so used + fsPromises.readFile + .mockResolvedValueOnce(JSON.stringify({ cwd: '/work/a/foo/bar' }) + '\n') + // For z-bar: read 789.jsonl -> throws parse or no cwd, then fallback decode of projectId (z-bar -> z/bar) + .mockResolvedValueOnce(JSON.stringify({ something: 'else' }) + '\n'); + + const projects = await svc.listProjects(); + + // Sorting: a-foo-bar most recent session mtime=2000, z-bar most recent session mtime=1200 + expect(projects.map(p => p.id)).toEqual(['a-foo-bar', 'z-bar']); + + const a = projects[0]; + expect(a.path).toBe('/work/a/foo/bar'); + expect(a.sessions.sort()).toEqual(['123', '456']); + expect(a.created_at).toBe(1000); + expect(a.most_recent_session).toBe(2000); + + const z = projects[1]; + expect(z.path).toBe('z/bar'); // fallback decodeProjectPath + expect(z.sessions).toEqual(['789']); + expect(z.created_at).toBe(900); + expect(z.most_recent_session).toBe(1200); + }); + + it('ignores unreadable projects and continues', async () => { + const svc = new ProjectService(HOME); + fsPromises.access.mockResolvedValueOnce(undefined); + fsPromises.readdir.mockResolvedValueOnce([ + dirent('good', 'dir'), + dirent('bad', 'dir'), + ]); + + // Good project + fsPromises.stat + .mockResolvedValueOnce(statWithTimes({ ctime: 100 })) + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 120 })); // session + + fsPromises.readdir + .mockResolvedValueOnce([dirent('s1.jsonl', 'file')]); + + fsPromises.readFile + .mockResolvedValueOnce(JSON.stringify({ cwd: '/work/good' }) + '\n'); + + // Bad project: throw while reading project dir stat + fsPromises.stat.mockRejectedValueOnce(new Error('stat error for bad')); + + const projects = await svc.listProjects(); + expect(projects).toHaveLength(1); + expect(projects[0].id).toBe('good'); + expect(projects[0].path).toBe('/work/good'); + }); + }); + + describe('createProject', () => { + it('creates project directory, writes metadata, and returns project data', async () => { + const svc = new ProjectService(HOME); + const path = '/repo/foo'; + const projectId = ' - repo - foo - '.trim().replace(/\//g, '-'); // Just a note. Actual code uses path.replace(/\//g, '-') + // Our expected id: + const expectedProjectId = path.replace(/\//g, '-'); + const projectDir = join(PROJECTS_DIR, expectedProjectId); + + // mkdir projects dir, then project dir + fsPromises.mkdir.mockResolvedValue(undefined); + + // stat(projectDir) for timestamps - prefer birthtime if available, else mtime + fsPromises.stat.mockResolvedValueOnce({ + ctime: fakeDate(0), // not used directly for createProject + mtime: fakeDate(2000), + birthtime: fakeDate(1500), + }); + + // write metadata.json + fsPromises.writeFile.mockResolvedValue(undefined); + + const result = await svc.createProject(path); + + expect(fsPromises.mkdir).toHaveBeenCalledWith(PROJECTS_DIR, { recursive: true }); + expect(fsPromises.mkdir).toHaveBeenCalledWith(projectDir, { recursive: true }); + // uses birthtime first then falls back + expect(result.created_at).toBe(1500); + expect(result).toEqual({ + id: expectedProjectId, + path, + sessions: [], + created_at: 1500, + }); + // verify metadata contents + const metadataPath = join(projectDir, 'metadata.json'); + const writeCall = fsPromises.writeFile.mock.calls.find(c => c[0] === metadataPath); + expect(writeCall).toBeTruthy(); + const json = JSON.parse(writeCall![1]); + expect(json).toEqual({ path, created_at: 1500, version: 1 }); + }); + + it('falls back to mtime when birthtime is unavailable', async () => { + const svc = new ProjectService(HOME); + fsPromises.mkdir.mockResolvedValue(undefined); + fsPromises.stat.mockResolvedValueOnce({ + ctime: fakeDate(0), + mtime: fakeDate(7777), + birthtime: undefined, + } as any); + fsPromises.writeFile.mockResolvedValue(undefined); + + const result = await svc.createProject('/x/y'); + expect(result.created_at).toBe(7777); + }); + }); + + describe('getProjectSessions', () => { + it('throws if project directory does not exist', async () => { + const svc = new ProjectService(HOME); + const projectId = 'x-y'; + const projectDir = join(PROJECTS_DIR, projectId); + fsPromises.access.mockRejectedValueOnce(Object.assign(new Error('no dir'), { code: 'ENOENT' })); + + await expect(svc.getProjectSessions(projectId)).rejects.toThrow(`Project directory not found: ${projectId}`); + expect(fsPromises.access).toHaveBeenCalledWith(projectDir); + }); + + it('returns sessions sorted by created_at (newest first) and extracts first user message and todos', async () => { + const svc = new ProjectService(HOME); + const projectId = 'foo-bar'; + const projectDir = join(PROJECTS_DIR, projectId); + + // access ok + fsPromises.access.mockResolvedValueOnce(undefined); + + // getProjectPathFromSessions: read first .jsonl file; include cwd + // readdir(projectDir) - for determining path + fsPromises.readdir + .mockResolvedValueOnce([dirent('100.jsonl', 'file'), dirent('50.jsonl', 'file')]) // for getProjectPathFromSessions + .mockResolvedValueOnce([dirent('100.jsonl', 'file'), dirent('50.jsonl', 'file')]); // for main sessions loop + + fsPromises.readFile + // For getProjectPathFromSessions -> pick first and read + .mockResolvedValueOnce(JSON.stringify({ cwd: '/work/foo/bar' }) + '\n') + + // For getSessionFromFile('100.jsonl') + .mockResolvedValueOnce([ + JSON.stringify({ message: { role: 'assistant', content: 'hi' }, timestamp: 't1' }), + JSON.stringify({ message: { role: 'user', content: 'User 100 message' }, timestamp: 't2' }), + '', + ].join('\n')) + + // For todos 100 + .mockResolvedValueOnce(JSON.stringify({ tasks: ['a', 'b'] })) + + // For getSessionFromFile('50.jsonl') + .mockResolvedValueOnce([ + JSON.stringify({ message: { role: 'user', content: [{ type: 'text', text: 'Array user message' }] }, timestamp: 't3' }), + '', + ].join('\n')) + + // For todos 50 -> simulate not found + .mockRejectedValueOnce(Object.assign(new Error('no todo'), { code: 'ENOENT' })); + + // stat for sessions: ctime defines created_at + fsPromises.stat + .mockResolvedValueOnce(statWithTimes({ ctime: 100 })) // for 100.jsonl + .mockResolvedValueOnce(statWithTimes({ ctime: 50 })); // for 50.jsonl + + const sessions = await svc.getProjectSessions(projectId); + + // Sorted newest first (100 then 50) + expect(sessions.map(s => s.id)).toEqual(['100', '50']); + + const s100 = sessions[0]; + expect(s100.project_id).toBe(projectId); + expect(s100.project_path).toBe('/work/foo/bar'); + expect(s100.created_at).toBe(100); + expect(s100.first_message).toBe('User 100 message'); + expect(s100.message_timestamp).toBe('t2'); + expect(s100.todo_data).toEqual({ tasks: ['a', 'b'] }); + + const s50 = sessions[1]; + expect(s50.created_at).toBe(50); + expect(s50.first_message).toBe('Array user message'); + expect(s50.todo_data).toBeUndefined(); + + // Verify readFile was attempted for todos in HOME/todos/.json + const todosPath100 = join(HOME, 'todos', '100.json'); + const todosReadCall = fsPromises.readFile.mock.calls.find(c => c[0] === todosPath100); + expect(todosReadCall).toBeTruthy(); + }); + + it('falls back to decodeProjectPath when getProjectPathFromSessions fails', async () => { + const svc = new ProjectService(HOME); + const projectId = 'a-b-c'; + fsPromises.access.mockResolvedValueOnce(undefined); + // readdir for getProjectPathFromSessions + fsPromises.readdir + .mockResolvedValueOnce([dirent('1.jsonl', 'file')]) // for getProjectPathFromSessions + .mockResolvedValueOnce([dirent('1.jsonl', 'file')]); // main + + fsPromises.readFile + // fail on reading session for path extraction + .mockRejectedValueOnce(new Error('parse fail')) + // for getSessionFromFile + .mockResolvedValueOnce(JSON.stringify({ message: { role: 'user', content: 'hello' }, timestamp: 't' })); + + fsPromises.stat.mockResolvedValueOnce(statWithTimes({ ctime: 1 })); + + const sessions = await svc.getProjectSessions(projectId); + expect(sessions).toHaveLength(1); + expect(sessions[0].project_path).toBe('a/b/c'); + }); + }); + + describe('loadSessionHistory', () => { + it('returns file content for found session across projects', async () => { + const svc = new ProjectService(HOME); + + // listProjects -> access ok + fsPromises.access.mockResolvedValueOnce(undefined); + + // readdir projects list: two projects + fsPromises.readdir + .mockResolvedValueOnce([dirent('p1', 'dir'), dirent('p2', 'dir')]) // listProjects + // getProjectFromDirectory('p1') entries + .mockResolvedValueOnce([dirent('111.jsonl', 'file')]) + // getProjectFromDirectory('p2') entries + .mockResolvedValueOnce([dirent('222.jsonl', 'file'), dirent('target.jsonl', 'file')]); + + // getProjectPathFromSessions for p1, p2 + fsPromises.readFile + .mockResolvedValueOnce(JSON.stringify({ cwd: '/work/p1' }) + '\n') + .mockResolvedValueOnce(JSON.stringify({ cwd: '/work/p2' }) + '\n'); + + // stats: project dirs and sessions to compute most_recent_session + fsPromises.stat + .mockResolvedValueOnce(statWithTimes({ ctime: 10 })) // p1 + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 100 })) // p1/111 + .mockResolvedValueOnce(statWithTimes({ ctime: 20 })) // p2 + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 90 })) // p2/222 + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 200 })); // p2/target + + // Once listProjects done, loadSessionHistory reads the target + fsPromises.readFile.mockResolvedValueOnce('HISTORY CONTENT'); + + const content = await svc.loadSessionHistory('target'); + expect(content).toBe('HISTORY CONTENT'); + const expectedPath = join(PROJECTS_DIR, 'p2', 'target.jsonl'); + expect(fsPromises.readFile).toHaveBeenCalledWith(expectedPath, 'utf-8'); + }); + + it('throws if session not found in any project', async () => { + const svc = new ProjectService(HOME); + + fsPromises.access.mockResolvedValueOnce(undefined); + fsPromises.readdir + .mockResolvedValueOnce([dirent('p', 'dir')]); + fsPromises.readdir + .mockResolvedValueOnce([dirent('111.jsonl', 'file')]); // getProjectFromDirectory + fsPromises.readFile + .mockResolvedValueOnce(JSON.stringify({ cwd: '/work/p' }) + '\n'); + + fsPromises.stat + .mockResolvedValueOnce(statWithTimes({ ctime: 1 })) // project dir + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 2 })); // session + + await expect(svc.loadSessionHistory('nope')).rejects.toThrow('Session not found: nope'); + }); + }); + + describe('findClaudeMdFiles', () => { + it('recursively finds CLAUDE.md files ignoring hidden directories', async () => { + const svc = new ProjectService(HOME); + const root = '/r'; + + // Directory tree: + // /r + // /sub1 + // CLAUDE.md + // file.txt + // /.hidden + // CLAUDE.md (should be ignored) + // /sub2 + // /nested + // CLAUDE.md + // CLAUDE.md + // + // Simulate traversal order with readdir per directory encountered + fsPromises.readdir + .mockResolvedValueOnce([dirent('sub1', 'dir'), dirent('.hidden', 'dir'), dirent('sub2', 'dir'), dirent('CLAUDE.md', 'file')]) // /r + .mockResolvedValueOnce([dirent('CLAUDE.md', 'file'), dirent('file.txt', 'file')]) // /r/sub1 + // .hidden should be attempted but ignored because name startsWith('.'); still a readdir call is made only if code enters it, but code avoids recursion into hidden dirs + .mockResolvedValueOnce([dirent('nested', 'dir')]) // /r/sub2 + .mockResolvedValueOnce([dirent('CLAUDE.md', 'file')]); // /r/sub2/nested + + // stat for each discovered CLAUDE.md + fsPromises.stat + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 100 })) // /r/CLAUDE.md + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 110 })) // /r/sub1/CLAUDE.md + .mockResolvedValueOnce(statWithTimes({ ctime: 0, mtime: 150 })); // /r/sub2/nested/CLAUDE.md + + const files = await svc.findClaudeMdFiles(root); + + // Expect 3 files (root, sub1, sub2/nested). Ensure properties filled. + const rel = files.map(f => f.relative_path).sort(); + expect(rel).toEqual(['CLAUDE.md', 'sub1/CLAUDE.md', 'sub2/nested/CLAUDE.md']); + + const rootFile = files.find(f => f.relative_path === 'CLAUDE.md')!; + expect(rootFile.absolute_path).toBe(join(root, 'CLAUDE.md')); + expect(rootFile.size).toBeDefined(); + expect(rootFile.modified).toBe(100); + + const nestedFile = files.find(f => f.relative_path === 'sub2/nested/CLAUDE.md')!; + expect(nestedFile.modified).toBe(150); + }); + + it('logs and continues when encountering unreadable directories', async () => { + const svc = new ProjectService(HOME); + const root = '/r'; + + // First call throws, to simulate unreadable root (function should catch and warn, then return []) + fsPromises.readdir.mockRejectedValueOnce(new Error('permission denied')); + + const files = await svc.findClaudeMdFiles(root); + expect(files).toEqual([]); + }); + }); + + describe('readClaudeMdFile', () => { + it('reads CLAUDE.md content', async () => { + const svc = new ProjectService(HOME); + fsPromises.readFile.mockResolvedValueOnce('# Hello'); + + const content = await svc.readClaudeMdFile('/p/CLAUDE.md'); + expect(content).toBe('# Hello'); + expect(fsPromises.readFile).toHaveBeenCalledWith('/p/CLAUDE.md', 'utf-8'); + }); + }); + + describe('saveClaudeMdFile', () => { + it('writes CLAUDE.md content', async () => { + const svc = new ProjectService(HOME); + fsPromises.writeFile.mockResolvedValueOnce(undefined); + + await svc.saveClaudeMdFile('/p/CLAUDE.md', 'Body'); + expect(fsPromises.writeFile).toHaveBeenCalledWith('/p/CLAUDE.md', 'Body', 'utf-8'); + }); + }); + + describe('listDirectoryContents', () => { + it('lists files and directories with details and sorts dirs first then by name', async () => { + const svc = new ProjectService(HOME); + const path = '/code'; + + fsPromises.readdir.mockResolvedValueOnce([ + dirent('b.txt', 'file'), + dirent('a', 'dir'), + dirent('z', 'dir'), + dirent('a.txt', 'file'), + ]); + + // stat for each full path in iteration order + fsPromises.stat + .mockResolvedValueOnce({ size: 22, isDirectory: () => false }) // b.txt + .mockResolvedValueOnce({ size: 0, isDirectory: () => true }) // a (dir) - ignored since entry.isDirectory() used + .mockResolvedValueOnce({ size: 0, isDirectory: () => true }) // z (dir) - ignored + .mockResolvedValueOnce({ size: 11, isDirectory: () => false }); // a.txt + + const result = await svc.listDirectoryContents(path); + + // Sorted: directories first (a, z), then files (a.txt, b.txt) alphabetically + expect(result.map(e => e.name)).toEqual(['a', 'z', 'a.txt', 'b.txt']); + + const aTxt = result.find(e => e.name === 'a.txt')!; + expect(aTxt.extension).toBe('txt'); + expect(aTxt.size).toBe(11); + expect(aTxt.is_directory).toBe(false); + + const aDir = result.find(e => e.name === 'a')!; + expect(aDir.is_directory).toBe(true); + expect(aDir.size).toBe(0); + expect(aDir.extension).toBeUndefined(); + }); + + it('skips files that cannot be stat-ed', async () => { + const svc = new ProjectService(HOME); + const path = '/code'; + + fsPromises.readdir.mockResolvedValueOnce([ + dirent('ok.txt', 'file'), + dirent('bad.txt', 'file'), + ]); + + fsPromises.stat + .mockResolvedValueOnce({ size: 5 }) // ok.txt + .mockRejectedValueOnce(new Error('stat failed')); // bad.txt + + const result = await svc.listDirectoryContents(path); + + expect(result.map(e => e.name)).toEqual(['ok.txt']); + }); + }); + + describe('internal behaviors', () => { + it('getProjectPathFromSessions selects first JSONL with cwd and throws when none provide it', async () => { + const svc = new ProjectService(HOME); + const dir = '/p'; + + // Entries: two jsonl files + fsPromises.readdir.mockResolvedValueOnce([ + dirent('1.jsonl', 'file'), + dirent('2.jsonl', 'file'), + dirent('readme.md', 'file'), + ]); + + // read 1 -> no cwd; read 2 -> has cwd + fsPromises.readFile + .mockResolvedValueOnce(JSON.stringify({}) + '\n') + .mockResolvedValueOnce(JSON.stringify({ cwd: '/work/p' }) + '\n'); + + // @ts-ignore accessing private method by casting to any for unit testing purposes + const path = await (svc as any).getProjectPathFromSessions(dir); + expect(path).toBe('/work/p'); + }); + + it('getProjectPathFromSessions throws if no sessions have cwd or files are unreadable', async () => { + const svc = new ProjectService(HOME); + const dir = '/p'; + + fsPromises.readdir.mockResolvedValueOnce([ + dirent('1.jsonl', 'file'), + dirent('2.jsonl', 'file'), + ]); + + fsPromises.readFile + .mockResolvedValueOnce(JSON.stringify({}) + '\n') + .mockResolvedValueOnce(JSON.stringify({ notcwd: true }) + '\n'); + + await expect((svc as any).getProjectPathFromSessions(dir)).rejects.toThrow('Could not determine project path from session files'); + }); + + it('decodeProjectPath decodes hyphens to slashes', () => { + const svc = new ProjectService(HOME); + // @ts-ignore + const decoded = (svc as any).decodeProjectPath('a-b-c'); + expect(decoded).toBe('a/b/c'); + }); + }); + +}); \ No newline at end of file diff --git a/claudia-server/src/services/project.ts b/claudia-server/src/services/project.ts new file mode 100644 index 000000000..c33d3d538 --- /dev/null +++ b/claudia-server/src/services/project.ts @@ -0,0 +1,391 @@ +import { promises as fs } from 'fs'; +import { join, basename, extname } from 'path'; +import { homedir } from 'os'; +import type { Project, Session, ClaudeMdFile, FileEntry } from '../types/index.js'; + +/** + * Service for managing Claude projects and sessions + */ +export class ProjectService { + private claudeHomeDir: string; + private projectsDir: string; + + constructor(claudeHomeDir?: string) { + this.claudeHomeDir = claudeHomeDir || join(homedir(), '.claude'); + this.projectsDir = join(this.claudeHomeDir, 'projects'); + } + + /** + * List all projects in ~/.claude/projects/ + */ + async listProjects(): Promise { + try { + await fs.access(this.projectsDir); + } catch { + // Projects directory doesn't exist + return []; + } + + const projectDirs = await fs.readdir(this.projectsDir, { withFileTypes: true }); + const projects: Project[] = []; + + for (const dirent of projectDirs) { + if (dirent.isDirectory()) { + try { + const project = await this.getProjectFromDirectory(dirent.name); + projects.push(project); + } catch (error) { + console.warn(`Failed to read project ${dirent.name}:`, error); + } + } + } + + // Sort projects by most recent session activity, then by creation time + projects.sort((a, b) => { + // First compare by most recent session + if (a.most_recent_session && b.most_recent_session) { + return b.most_recent_session - a.most_recent_session; + } + if (a.most_recent_session && !b.most_recent_session) return -1; + if (!a.most_recent_session && b.most_recent_session) return 1; + return b.created_at - a.created_at; + }); + + return projects; + } + + /** + * Get project from directory name + */ + private async getProjectFromDirectory(projectId: string): Promise { + const projectDir = join(this.projectsDir, projectId); + const stats = await fs.stat(projectDir); + + // Get sessions (JSONL files) + const entries = await fs.readdir(projectDir, { withFileTypes: true }); + const sessions = entries + .filter(entry => entry.isFile() && entry.name.endsWith('.jsonl')) + .map(entry => basename(entry.name, '.jsonl')); + + // Get actual project path from session files + let projectPath: string; + try { + projectPath = await this.getProjectPathFromSessions(projectDir); + } catch { + // Fallback to decoded path + projectPath = this.decodeProjectPath(projectId); + } + + // Find most recent session + let mostRecentSession: number | undefined; + for (const sessionId of sessions) { + try { + const sessionPath = join(projectDir, `${sessionId}.jsonl`); + const sessionStats = await fs.stat(sessionPath); + const sessionTime = Math.floor(sessionStats.mtime.getTime() / 1000); + + if (!mostRecentSession || sessionTime > mostRecentSession) { + mostRecentSession = sessionTime; + } + } catch { + // Ignore errors reading individual sessions + } + } + + return { + id: projectId, + path: projectPath, + sessions, + created_at: Math.floor(stats.ctime.getTime() / 1000), + most_recent_session: mostRecentSession, + }; + } + + /** + * Get actual project path by reading from JSONL files + */ + private async getProjectPathFromSessions(projectDir: string): Promise { + const entries = await fs.readdir(projectDir, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isFile() && entry.name.endsWith('.jsonl')) { + try { + const filePath = join(projectDir, entry.name); + const content = await fs.readFile(filePath, 'utf-8'); + const lines = content.split('\n').filter(line => line.trim()); + + if (lines.length > 0) { + const firstLine = JSON.parse(lines[0]); + if (firstLine.cwd) { + return firstLine.cwd; + } + } + } catch { + continue; + } + } + } + + throw new Error('Could not determine project path from session files'); + } + + /** + * Decode project directory name back to original path (fallback) + */ + private decodeProjectPath(encoded: string): string { + return encoded.replace(/-/g, '/'); + } + + /** + * Create a new project for the given directory path + */ + async createProject(path: string): Promise { + // Encode the path to create a project ID + const projectId = path.replace(/\//g, '-'); + + // Create projects directory if it doesn't exist + await fs.mkdir(this.projectsDir, { recursive: true }); + + // Create project directory if it doesn't exist + const projectDir = join(this.projectsDir, projectId); + await fs.mkdir(projectDir, { recursive: true }); + + // Get creation time (prefer birthtime if available, else mtime) + const stats = await fs.stat(projectDir); + const createdAt = Math.floor( + ((stats.birthtime?.getTime?.() ?? 0) || stats.mtime.getTime()) / 1000 + ); + + // Persist metadata for reliable path and timestamps + const metadataPath = join(projectDir, 'metadata.json'); + await fs.writeFile( + metadataPath, + JSON.stringify({ path, created_at: createdAt, version: 1 }, null, 2), + 'utf-8' + ); + + return { + id: projectId, + path, + sessions: [], + created_at: createdAt, + }; + } + + /** + * Get sessions for a specific project + */ + async getProjectSessions(projectId: string): Promise { + const projectDir = join(this.projectsDir, projectId); + + try { + await fs.access(projectDir); + } catch { + throw new Error(`Project directory not found: ${projectId}`); + } + + // Get the actual project path + let projectPath: string; + try { + projectPath = await this.getProjectPathFromSessions(projectDir); + } catch { + projectPath = this.decodeProjectPath(projectId); + } + + const sessions: Session[] = []; + const entries = await fs.readdir(projectDir, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isFile() && entry.name.endsWith('.jsonl')) { + try { + const sessionId = basename(entry.name, '.jsonl'); + const session = await this.getSessionFromFile(projectId, projectPath, sessionId); + sessions.push(session); + } catch (error) { + console.warn(`Failed to read session ${entry.name}:`, error); + } + } + } + + // Sort sessions by creation time (newest first) + sessions.sort((a, b) => b.created_at - a.created_at); + + return sessions; + } + + /** + * Get session metadata from JSONL file + */ + private async getSessionFromFile(projectId: string, projectPath: string, sessionId: string): Promise { + const sessionPath = join(this.projectsDir, projectId, `${sessionId}.jsonl`); + const stats = await fs.stat(sessionPath); + + let firstMessage: string | undefined; + let messageTimestamp: string | undefined; + let todoData: any; + + try { + const content = await fs.readFile(sessionPath, 'utf-8'); + const lines = content.split('\n').filter(line => line.trim()); + + // Find first user message + for (const line of lines) { + try { + const entry = JSON.parse(line); + if (entry.message?.role === 'user' && entry.message?.content) { + firstMessage = Array.isArray(entry.message.content) + ? entry.message.content.find((c: any) => c.type === 'text')?.text + : entry.message.content; + messageTimestamp = entry.timestamp; + break; + } + } catch { + continue; + } + } + + // Check for todo data + const todosDir = join(this.claudeHomeDir, 'todos'); + try { + const todoPath = join(todosDir, `${sessionId}.json`); + const todoContent = await fs.readFile(todoPath, 'utf-8'); + todoData = JSON.parse(todoContent); + } catch { + // No todo data + } + } catch (error) { + console.warn(`Failed to parse session content for ${sessionId}:`, error); + } + + return { + id: sessionId, + project_id: projectId, + project_path: projectPath, + created_at: Math.floor(stats.ctime.getTime() / 1000), + first_message: firstMessage, + message_timestamp: messageTimestamp, + todo_data: todoData, + }; + } + + /** + * Load session history/output + */ + async loadSessionHistory(sessionId: string): Promise { + // Find session in any project + const projects = await this.listProjects(); + + for (const project of projects) { + if (project.sessions.includes(sessionId)) { + const sessionPath = join(this.projectsDir, project.id, `${sessionId}.jsonl`); + try { + return await fs.readFile(sessionPath, 'utf-8'); + } catch { + continue; + } + } + } + + throw new Error(`Session not found: ${sessionId}`); + } + + /** + * Find CLAUDE.md files in a project + */ + async findClaudeMdFiles(projectPath: string): Promise { + const files: ClaudeMdFile[] = []; + + try { + await this.findClaudeMdFilesRecursive(projectPath, projectPath, files); + } catch (error) { + console.warn(`Failed to scan project for CLAUDE.md files:`, error); + } + + return files; + } + + /** + * Recursively find CLAUDE.md files + */ + private async findClaudeMdFilesRecursive( + rootPath: string, + currentPath: string, + files: ClaudeMdFile[] + ): Promise { + try { + const entries = await fs.readdir(currentPath, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(currentPath, entry.name); + + if (entry.isDirectory() && !entry.name.startsWith('.')) { + // Recurse into subdirectories (except hidden ones) + await this.findClaudeMdFilesRecursive(rootPath, fullPath, files); + } else if (entry.isFile() && entry.name.toLowerCase() === 'claude.md') { + const stats = await fs.stat(fullPath); + const relativePath = fullPath.substring(rootPath.length + 1); + + files.push({ + relative_path: relativePath, + absolute_path: fullPath, + size: stats.size, + modified: Math.floor(stats.mtime.getTime() / 1000), + }); + } + } + } catch (error) { + // Skip directories we can't read + console.warn(`Cannot read directory ${currentPath}:`, error); + } + } + + /** + * Read CLAUDE.md file content + */ + async readClaudeMdFile(filePath: string): Promise { + return await fs.readFile(filePath, 'utf-8'); + } + + /** + * Save CLAUDE.md file content + */ + async saveClaudeMdFile(filePath: string, content: string): Promise { + await fs.writeFile(filePath, content, 'utf-8'); + } + + /** + * List directory contents + */ + async listDirectoryContents(path: string): Promise { + const entries = await fs.readdir(path, { withFileTypes: true }); + const fileEntries: FileEntry[] = []; + + for (const entry of entries) { + const fullPath = join(path, entry.name); + + try { + const stats = await fs.stat(fullPath); + + fileEntries.push({ + name: entry.name, + path: fullPath, + is_directory: entry.isDirectory(), + size: entry.isDirectory() ? 0 : stats.size, + extension: entry.isDirectory() ? undefined : extname(entry.name).substring(1), + }); + } catch (error) { + // Skip files we can't stat + console.warn(`Cannot stat ${fullPath}:`, error); + } + } + + // Sort directories first, then files, alphabetically + fileEntries.sort((a, b) => { + if (a.is_directory && !b.is_directory) return -1; + if (!a.is_directory && b.is_directory) return 1; + return a.name.localeCompare(b.name); + }); + + return fileEntries; + } +} \ No newline at end of file diff --git a/claudia-server/src/services/websocket.ts b/claudia-server/src/services/websocket.ts new file mode 100644 index 000000000..5ac4ff0b7 --- /dev/null +++ b/claudia-server/src/services/websocket.ts @@ -0,0 +1,212 @@ +import { WebSocketServer, WebSocket } from 'ws'; +import { EventEmitter } from 'events'; +import type { WebSocketMessage } from '../types/index.js'; + +/** + * Service for managing WebSocket connections and real-time communication + */ +export class WebSocketService extends EventEmitter { + private wss: WebSocketServer; + private clients: Map = new Map(); + private subscriptions: Map> = new Map(); // clientId -> sessionIds + + constructor(server: any) { + super(); + + this.wss = new WebSocketServer({ + server, + path: '/ws' + }); + + this.setupWebSocketServer(); + } + + private setupWebSocketServer(): void { + this.wss.on('connection', (ws: WebSocket, request) => { + const clientId = this.generateClientId(); + this.clients.set(clientId, ws); + this.subscriptions.set(clientId, new Set()); + + console.log(`WebSocket client connected: ${clientId}`); + + // Send welcome message + this.sendToClient(clientId, { + type: 'status', + data: { status: 'connected', client_id: clientId }, + timestamp: new Date().toISOString(), + }); + + ws.on('message', (data: Buffer) => { + try { + const message = JSON.parse(data.toString()) as WebSocketMessage; + this.handleClientMessage(clientId, message); + } catch (error) { + this.sendError(clientId, 'Invalid JSON message', error); + } + }); + + ws.on('close', () => { + console.log(`WebSocket client disconnected: ${clientId}`); + this.clients.delete(clientId); + this.subscriptions.delete(clientId); + }); + + ws.on('error', (error: Error) => { + console.error(`WebSocket error for client ${clientId}:`, error); + this.clients.delete(clientId); + this.subscriptions.delete(clientId); + }); + }); + } + + private generateClientId(): string { + return `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + } + + private handleClientMessage(clientId: string, message: WebSocketMessage): void { + switch (message.type) { + case 'subscribe': + this.handleSubscribe(clientId, message); + break; + case 'unsubscribe': + this.handleUnsubscribe(clientId, message); + break; + default: + this.sendError(clientId, 'Unknown message type', { type: message.type }); + } + } + + private handleSubscribe(clientId: string, message: WebSocketMessage): void { + if (!message.session_id) { + this.sendError(clientId, 'session_id required for subscribe'); + return; + } + + const subscriptions = this.subscriptions.get(clientId); + if (subscriptions) { + subscriptions.add(message.session_id); + console.log(`Client ${clientId} subscribed to session ${message.session_id}`); + + this.sendToClient(clientId, { + type: 'status', + data: { + status: 'subscribed', + session_id: message.session_id, + subscriptions: Array.from(subscriptions) + }, + timestamp: new Date().toISOString(), + }); + } + } + + private handleUnsubscribe(clientId: string, message: WebSocketMessage): void { + if (!message.session_id) { + this.sendError(clientId, 'session_id required for unsubscribe'); + return; + } + + const subscriptions = this.subscriptions.get(clientId); + if (subscriptions) { + subscriptions.delete(message.session_id); + console.log(`Client ${clientId} unsubscribed from session ${message.session_id}`); + + this.sendToClient(clientId, { + type: 'status', + data: { + status: 'unsubscribed', + session_id: message.session_id, + subscriptions: Array.from(subscriptions) + }, + timestamp: new Date().toISOString(), + }); + } + } + + /** + * Send message to specific client + */ + private sendToClient(clientId: string, message: WebSocketMessage): void { + const client = this.clients.get(clientId); + if (client && client.readyState === WebSocket.OPEN) { + try { + client.send(JSON.stringify(message)); + } catch (error) { + console.error(`Failed to send message to client ${clientId}:`, error); + } + } + } + + /** + * Send error message to client + */ + private sendError(clientId: string, error: string, details?: any): void { + this.sendToClient(clientId, { + type: 'error', + data: { error, details }, + timestamp: new Date().toISOString(), + }); + } + + /** + * Broadcast Claude stream message to subscribed clients + */ + broadcastClaudeStream(sessionId: string, message: any): void { + const wsMessage: WebSocketMessage = { + type: 'claude_stream', + data: message, + session_id: sessionId, + timestamp: new Date().toISOString(), + }; + + for (const [clientId, subscriptions] of this.subscriptions.entries()) { + if (subscriptions.has(sessionId)) { + this.sendToClient(clientId, wsMessage); + } + } + } + + /** + * Broadcast status message to all clients + */ + broadcastStatus(data: any): void { + const message: WebSocketMessage = { + type: 'status', + data, + timestamp: new Date().toISOString(), + }; + + for (const clientId of this.clients.keys()) { + this.sendToClient(clientId, message); + } + } + + /** + * Get connected clients count + */ + getConnectedClientsCount(): number { + return this.clients.size; + } + + /** + * Get active subscriptions + */ + getActiveSubscriptions(): Record { + const result: Record = {}; + for (const [clientId, subscriptions] of this.subscriptions.entries()) { + result[clientId] = Array.from(subscriptions); + } + return result; + } + + /** + * Close all connections and cleanup + */ + close(): void { + for (const client of this.clients.values()) { + client.close(); + } + this.clients.clear(); + this.subscriptions.clear(); + this.wss.close(); + } +} \ No newline at end of file diff --git a/claudia-server/src/types/index.ts b/claudia-server/src/types/index.ts new file mode 100644 index 000000000..38c41d296 --- /dev/null +++ b/claudia-server/src/types/index.ts @@ -0,0 +1,179 @@ +/** + * Core type definitions for Claudia Server + * These mirror the types from the original Tauri app + */ + +export type ProcessType = + | { AgentRun: { agent_id: number; agent_name: string } } + | { ClaudeSession: { session_id: string } }; + +export interface ProcessInfo { + run_id: number; + process_type: ProcessType; + pid: number; + started_at: string; + project_path: string; + task: string; + model: string; +} + +/** + * Represents a project in the ~/.claude/projects directory + */ +export interface Project { + /** The project ID (derived from the directory name) */ + id: string; + /** The original project path (decoded from the directory name) */ + path: string; + /** List of session IDs (JSONL file names without extension) */ + sessions: string[]; + /** Unix timestamp when the project directory was created */ + created_at: number; + /** Unix timestamp of the most recent session (if any) */ + most_recent_session?: number; +} + +/** + * Represents a session with its metadata + */ +export interface Session { + /** The session ID (UUID) */ + id: string; + /** The project ID this session belongs to */ + project_id: string; + /** The project path */ + project_path: string; + /** Optional todo data associated with this session */ + todo_data?: any; + /** Unix timestamp when the session file was created */ + created_at: number; + /** First user message content (if available) */ + first_message?: string; + /** Timestamp of the first user message (if available) */ + message_timestamp?: string; +} + +/** + * Represents the settings from ~/.claude/settings.json + */ +export interface ClaudeSettings { + [key: string]: any; +} + +/** + * Represents the Claude Code version status + */ +export interface ClaudeVersionStatus { + /** Whether Claude Code is installed and working */ + is_installed: boolean; + /** The version string if available */ + version?: string; + /** The full output from the command */ + output: string; +} + +/** + * Represents a CLAUDE.md file found in the project + */ +export interface ClaudeMdFile { + /** Relative path from the project root */ + relative_path: string; + /** Absolute path to the file */ + absolute_path: string; + /** File size in bytes */ + size: number; + /** Last modified timestamp */ + modified: number; +} + +/** + * Represents a file or directory entry + */ +export interface FileEntry { + /** The name of the file or directory */ + name: string; + /** The full path */ + path: string; + /** Whether this is a directory */ + is_directory: boolean; + /** File size in bytes (0 for directories) */ + size: number; + /** File extension (if applicable) */ + extension?: string; +} + +/** + * Claude streaming message types + */ +export interface ClaudeStreamMessage { + type: 'start' | 'partial' | 'complete' | 'error'; + content?: string; + role?: 'user' | 'assistant'; + tool_calls?: any[]; + timestamp: string; + session_id?: string; +} + +/** + * API Request types + */ +export interface ExecuteClaudeRequest { + project_path: string; + prompt: string; + model: string; +} + +export interface ContinueClaudeRequest { + project_path: string; + prompt: string; + model: string; +} + +export interface ResumeClaudeRequest { + project_path: string; + session_id: string; + prompt: string; + model: string; +} + +/** + * WebSocket message types + */ +export interface WebSocketMessage { + type: 'subscribe' | 'unsubscribe' | 'claude_stream' | 'error' | 'status'; + data?: any; + session_id?: string; + timestamp: string; +} + +/** + * Server configuration + */ +export interface ServerConfig { + port: number; + host: string; + cors_origin: string[]; + max_concurrent_sessions: number; + session_timeout_ms: number; + claude_binary_path?: string; + claude_home_dir?: string; +} + +/** + * Error response type + */ +export interface ErrorResponse { + error: string; + code: string; + timestamp: string; + details?: any; +} + +/** + * Success response type + */ +export interface SuccessResponse { + success: true; + data: T; + timestamp: string; +} \ No newline at end of file diff --git a/claudia-server/test.sh b/claudia-server/test.sh new file mode 100755 index 000000000..f67ed979c --- /dev/null +++ b/claudia-server/test.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# Simple integration test for Claudia Server +# Tests basic functionality without requiring Claude Code CLI + +set -e + +echo "🧪 Claudia Server Integration Test" +echo "==================================" + +SERVER_PORT=3099 +SERVER_URL="http://localhost:$SERVER_PORT" +LOG_FILE="/tmp/claudia-server-test.log" + +# Start server in background +echo "Starting server on port $SERVER_PORT..." +cd "$(dirname "$0")" +node dist/index.js --port $SERVER_PORT > "$LOG_FILE" 2>&1 & +SERVER_PID=$! + +# Wait for server to start with health check +echo "Waiting for server to start..." +for i in {1..30}; do + if curl -s "http://localhost:$SERVER_PORT/api/status/health" >/dev/null 2>&1; then + echo "Server started successfully" + break + fi + if [ $i -eq 30 ]; then + echo "❌ Server failed to start within 30 seconds" + kill $SERVER_PID 2>/dev/null || true + exit 1 + fi + sleep 1 +done +# Test health endpoint +echo "✅ Testing health endpoint..." +response=$(curl -s -w "%{http_code}" "$SERVER_URL/api/status/health" -o /tmp/health_response.json) +if [ "$response" != "200" ]; then + echo "❌ Health check failed (HTTP $response)" + exit 1 +fi + +health_status=$(jq -r '.data.status' /tmp/health_response.json) +if [ "$health_status" != "healthy" ]; then + echo "❌ Server is not healthy: $health_status" + exit 1 +fi +echo " Server is healthy ✓" + +# Test info endpoint +echo "✅ Testing info endpoint..." +response=$(curl -s -w "%{http_code}" "$SERVER_URL/api/status/info" -o /tmp/info_response.json) +if [ "$response" != "200" ]; then + echo "❌ Info endpoint failed (HTTP $response)" + exit 1 +fi + +server_name=$(jq -r '.data.name' /tmp/info_response.json) +if [ "$server_name" != "Claudia Server" ]; then + echo "❌ Unexpected server name: $server_name" + exit 1 +fi +echo " Info endpoint working ✓" + +# Test Claude version endpoint (this will fail if Claude CLI is not installed, but that's expected) +echo "✅ Testing Claude version endpoint..." +response=$(curl -s -w "%{http_code}" "$SERVER_URL/api/claude/version" -o /tmp/claude_response.json) +echo " Claude version check returned HTTP $response" +if [ "$response" = "200" ]; then + is_installed=$(jq -r '.data.is_installed' /tmp/claude_response.json) + echo " Claude installed: $is_installed ✓" +else + echo " Claude version endpoint accessible ✓" +fi + +# Test projects endpoint +echo "✅ Testing projects endpoint..." +response=$(curl -s -w "%{http_code}" "$SERVER_URL/api/projects" -o /tmp/projects_response.json) +if [ "$response" != "200" ]; then + echo "❌ Projects endpoint failed (HTTP $response)" + exit 1 +fi +echo " Projects endpoint working ✓" + +# Test WebSocket endpoint (basic connection test) +echo "✅ Testing WebSocket endpoint..." +if command -v wscat >/dev/null 2>&1; then + echo '{"type":"status"}' | timeout 2s wscat -c "ws://localhost:$SERVER_PORT/ws" >/dev/null 2>&1 && echo " WebSocket endpoint accessible ✓" || echo " WebSocket test skipped (connection failed)" +else + echo " WebSocket test skipped (wscat not available)" +fi + +# Test 404 handling +echo "✅ Testing 404 handling..." +response=$(curl -s -w "%{http_code}" "$SERVER_URL/api/nonexistent" -o /tmp/404_response.json) +if [ "$response" != "404" ]; then + echo "❌ 404 handling failed (expected 404, got $response)" + exit 1 +fi +echo " 404 handling working ✓" + +# Test CORS headers +echo "✅ Testing CORS headers..." +cors_header=$(curl -s -D - "$SERVER_URL/api/status/health" | grep -i "access-control-allow-origin" || true) +if [ -n "$cors_header" ]; then + echo " CORS headers present ✓" +else + echo " CORS headers missing (may be expected)" +fi + +# Cleanup +echo "🧹 Cleaning up..." +kill $SERVER_PID >/dev/null 2>&1 || true +rm -f /tmp/health_response.json /tmp/info_response.json /tmp/claude_response.json /tmp/projects_response.json /tmp/404_response.json + +echo "" +echo "🎉 All tests passed!" +echo " Server is working correctly on port $SERVER_PORT" +echo " Log file: $LOG_FILE" +echo "" +echo "📝 Next steps:" +echo " 1. Install Claude Code CLI for full functionality" +echo " 2. Start the server: npm start" +echo " 3. Try the examples in examples/ directory" +echo " 4. Read the documentation in README.md" \ No newline at end of file diff --git a/claudia-server/tsconfig.json b/claudia-server/tsconfig.json new file mode 100644 index 000000000..e6f89a0df --- /dev/null +++ b/claudia-server/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "removeComments": false, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "allowImportingTsExtensions": false, + "noEmit": false, + "types": ["node", "jest"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b22ccfe9b..3332e058b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,14 +37,18 @@ "date-fns": "^3.6.0", "diff": "^8.0.2", "framer-motion": "^12.0.0-alpha.1", + "hast-util-sanitize": "^5.0.2", "html2canvas": "^1.4.1", "lucide-react": "^0.468.0", + "posthog-js": "^1.258.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.54.2", "react-markdown": "^9.0.3", "react-syntax-highlighter": "^15.6.1", "recharts": "^2.14.1", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.0", "tailwind-merge": "^2.6.0", "tailwindcss": "^4.1.8", @@ -52,7 +56,7 @@ "zustand": "^5.0.6" }, "devDependencies": { - "@tauri-apps/cli": "^2", + "@tauri-apps/cli": "^2.7.1", "@types/node": "^22.15.30", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", @@ -65,6 +69,8 @@ }, "node_modules/@ampproject/remapping": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -76,6 +82,8 @@ }, "node_modules/@babel/code-frame": { "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { @@ -88,7 +96,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.27.5", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, "license": "MIT", "engines": { @@ -96,20 +106,22 @@ } }, "node_modules/@babel/core": { - "version": "7.27.4", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", + "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.4", - "@babel/parser": "^7.27.4", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.4", - "@babel/types": "^7.27.3", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -124,23 +136,17 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { - "version": "7.27.5", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -149,6 +155,8 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { @@ -162,16 +170,20 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", + "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": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { @@ -184,6 +196,8 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "dependencies": { @@ -200,6 +214,8 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -208,6 +224,8 @@ }, "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": { @@ -216,6 +234,8 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -224,6 +244,8 @@ }, "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": { @@ -231,23 +253,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.27.6", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", + "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.5", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -258,6 +284,8 @@ }, "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": { @@ -272,6 +300,8 @@ }, "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": { @@ -285,7 +315,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -293,6 +325,8 @@ }, "node_modules/@babel/template": { "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { @@ -305,24 +339,28 @@ } }, "node_modules/@babel/traverse": { - "version": "7.27.4", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.27.6", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -333,8 +371,84 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -347,177 +461,1187 @@ "node": ">=18" } }, - "node_modules/@floating-ui/core": { - "version": "1.7.1", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.9" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/dom": { - "version": "1.7.1", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.1", - "@floating-ui/utils": "^0.2.9" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.3", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.9", - "license": "MIT" - }, - "node_modules/@hookform/resolvers": { - "version": "3.10.0", + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], "license": "MIT", - "peerDependencies": { - "react-hook-form": "^7.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], - "dev": true, - "license": "LGPL-3.0-or-later", + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "license": "MIT" + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@parcel/watcher": { - "version": "2.5.1", - "hasInstallScript": true, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" + "node": ">=18" } }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.1", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ - "darwin" + "netbsd" ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=18" } }, - "node_modules/@parcel/watcher/node_modules/detect-libc": { - "version": "1.0.3", - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=0.10" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz", + "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.3" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@hookform/resolvers": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.4" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@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==", + "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==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/@radix-ui/number": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", "license": "MIT" }, "node_modules/@radix-ui/primitive": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", "license": "MIT" }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3" @@ -539,6 +1663,8 @@ }, "node_modules/@radix-ui/react-collection": { "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -563,6 +1689,8 @@ }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -576,6 +1704,8 @@ }, "node_modules/@radix-ui/react-context": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -589,6 +1719,8 @@ }, "node_modules/@radix-ui/react-dialog": { "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -623,6 +1755,8 @@ }, "node_modules/@radix-ui/react-direction": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -636,6 +1770,8 @@ }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -661,6 +1797,8 @@ }, "node_modules/@radix-ui/react-dropdown-menu": { "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", + "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -688,6 +1826,8 @@ }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -701,6 +1841,8 @@ }, "node_modules/@radix-ui/react-focus-scope": { "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -724,6 +1866,8 @@ }, "node_modules/@radix-ui/react-id": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" @@ -740,6 +1884,8 @@ }, "node_modules/@radix-ui/react-label": { "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3" @@ -761,6 +1907,8 @@ }, "node_modules/@radix-ui/react-menu": { "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", + "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -799,6 +1947,8 @@ }, "node_modules/@radix-ui/react-popover": { "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -834,6 +1984,8 @@ }, "node_modules/@radix-ui/react-popper": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", @@ -864,6 +2016,8 @@ }, "node_modules/@radix-ui/react-portal": { "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3", @@ -886,6 +2040,8 @@ }, "node_modules/@radix-ui/react-presence": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -908,6 +2064,8 @@ }, "node_modules/@radix-ui/react-primitive": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { "@radix-ui/react-slot": "1.2.3" @@ -929,6 +2087,8 @@ }, "node_modules/@radix-ui/react-radio-group": { "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.7.tgz", + "integrity": "sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -959,6 +2119,8 @@ }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -988,6 +2150,8 @@ }, "node_modules/@radix-ui/react-select": { "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", @@ -1029,6 +2193,8 @@ }, "node_modules/@radix-ui/react-slot": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" @@ -1045,6 +2211,8 @@ }, "node_modules/@radix-ui/react-switch": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz", + "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -1072,6 +2240,8 @@ }, "node_modules/@radix-ui/react-tabs": { "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", + "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -1100,6 +2270,8 @@ }, "node_modules/@radix-ui/react-toast": { "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.14.tgz", + "integrity": "sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -1132,6 +2304,8 @@ }, "node_modules/@radix-ui/react-tooltip": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz", + "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -1164,6 +2338,8 @@ }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1177,6 +2353,8 @@ }, "node_modules/@radix-ui/react-use-controllable-state": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", @@ -1194,6 +2372,8 @@ }, "node_modules/@radix-ui/react-use-effect-event": { "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" @@ -1210,6 +2390,8 @@ }, "node_modules/@radix-ui/react-use-escape-keydown": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" @@ -1226,6 +2408,8 @@ }, "node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1239,6 +2423,8 @@ }, "node_modules/@radix-ui/react-use-previous": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1252,6 +2438,8 @@ }, "node_modules/@radix-ui/react-use-rect": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "license": "MIT", "dependencies": { "@radix-ui/rect": "1.1.1" @@ -1268,6 +2456,8 @@ }, "node_modules/@radix-ui/react-use-size": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" @@ -1284,6 +2474,8 @@ }, "node_modules/@radix-ui/react-visually-hidden": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.1.3" @@ -1305,15 +2497,47 @@ }, "node_modules/@radix-ui/rect": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.11", + "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.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.43.0", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", "cpu": [ "arm64" ], @@ -1323,24 +2547,249 @@ "darwin" ] }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@tailwindcss/cli": { - "version": "4.1.10", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.1.11.tgz", + "integrity": "sha512-7RAFOrVaXCFz5ooEG36Kbh+sMJiI2j4+Ozp71smgjnLfBRu7DTfoq8DsTvzse2/6nDeo2M3vS/FGaxfDgr3rtQ==", "license": "MIT", "dependencies": { "@parcel/watcher": "^2.5.1", - "@tailwindcss/node": "4.1.10", - "@tailwindcss/oxide": "4.1.10", + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", "enhanced-resolve": "^5.18.1", "mri": "^1.2.0", "picocolors": "^1.1.1", - "tailwindcss": "4.1.10" + "tailwindcss": "4.1.11" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "node_modules/@tailwindcss/node": { - "version": "4.1.10", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -1349,11 +2798,13 @@ "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.10" + "tailwindcss": "4.1.11" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.10", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -1364,22 +2815,40 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.10", - "@tailwindcss/oxide-darwin-arm64": "4.1.10", - "@tailwindcss/oxide-darwin-x64": "4.1.10", - "@tailwindcss/oxide-freebsd-x64": "4.1.10", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.10", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.10", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.10", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.10", - "@tailwindcss/oxide-linux-x64-musl": "4.1.10", - "@tailwindcss/oxide-wasm32-wasi": "4.1.10", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.10", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.10" + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.10", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", "cpu": [ "arm64" ], @@ -1392,23 +2861,209 @@ "node": ">= 10" } }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/@tailwindcss/vite": { - "version": "4.1.10", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", + "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.10", - "@tailwindcss/oxide": "4.1.10", - "tailwindcss": "4.1.10" + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "tailwindcss": "4.1.11" }, "peerDependencies": { - "vite": "^5.2.0 || ^6" + "vite": "^5.2.0 || ^6 || ^7" } }, "node_modules/@tanstack/react-virtual": { - "version": "3.13.10", + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", + "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.13.10" + "@tanstack/virtual-core": "3.13.12" }, "funding": { "type": "github", @@ -1420,7 +3075,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.13.10", + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", "license": "MIT", "funding": { "type": "github", @@ -1428,7 +3085,9 @@ } }, "node_modules/@tauri-apps/api": { - "version": "2.5.0", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.7.0.tgz", + "integrity": "sha512-v7fVE8jqBl8xJFOcBafDzXFc8FnicoH3j8o8DNNs0tHuEBmXUDqrCOAzMRX0UkfpwqZLqvrvK0GNQ45DfnoVDg==", "license": "Apache-2.0 OR MIT", "funding": { "type": "opencollective", @@ -1436,7 +3095,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.5.0", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.7.1.tgz", + "integrity": "sha512-RcGWR4jOUEl92w3uvI0h61Llkfj9lwGD1iwvDRD2isMrDhOzjeeeVn9aGzeW1jubQ/kAbMYfydcA4BA0Cy733Q==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -1450,21 +3111,23 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.5.0", - "@tauri-apps/cli-darwin-x64": "2.5.0", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.5.0", - "@tauri-apps/cli-linux-arm64-gnu": "2.5.0", - "@tauri-apps/cli-linux-arm64-musl": "2.5.0", - "@tauri-apps/cli-linux-riscv64-gnu": "2.5.0", - "@tauri-apps/cli-linux-x64-gnu": "2.5.0", - "@tauri-apps/cli-linux-x64-musl": "2.5.0", - "@tauri-apps/cli-win32-arm64-msvc": "2.5.0", - "@tauri-apps/cli-win32-ia32-msvc": "2.5.0", - "@tauri-apps/cli-win32-x64-msvc": "2.5.0" + "@tauri-apps/cli-darwin-arm64": "2.7.1", + "@tauri-apps/cli-darwin-x64": "2.7.1", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.7.1", + "@tauri-apps/cli-linux-arm64-gnu": "2.7.1", + "@tauri-apps/cli-linux-arm64-musl": "2.7.1", + "@tauri-apps/cli-linux-riscv64-gnu": "2.7.1", + "@tauri-apps/cli-linux-x64-gnu": "2.7.1", + "@tauri-apps/cli-linux-x64-musl": "2.7.1", + "@tauri-apps/cli-win32-arm64-msvc": "2.7.1", + "@tauri-apps/cli-win32-ia32-msvc": "2.7.1", + "@tauri-apps/cli-win32-x64-msvc": "2.7.1" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.5.0", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.7.1.tgz", + "integrity": "sha512-j2NXQN6+08G03xYiyKDKqbCV2Txt+hUKg0a8hYr92AmoCU8fgCjHyva/p16lGFGUG3P2Yu0xiNe1hXL9ZuRMzA==", "cpu": [ "arm64" ], @@ -1478,36 +3141,216 @@ "node": ">= 10" } }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.7.1.tgz", + "integrity": "sha512-CdYAefeM35zKsc91qIyKzbaO7FhzTyWKsE8hj7tEJ1INYpoh1NeNNyL/NSEA3Nebi5ilugioJ5tRK8ZXG8y3gw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.7.1.tgz", + "integrity": "sha512-dnvyJrTA1UJxJjQ8q1N/gWomjP8Twij1BUQu2fdcT3OPpqlrbOk5R1yT0oD/721xoKNjroB5BXCsmmlykllxNg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.7.1.tgz", + "integrity": "sha512-FtBW6LJPNRTws3qyUc294AqCWU91l/H0SsFKq6q4Q45MSS4x6wxLxou8zB53tLDGEPx3JSoPLcDaSfPlSbyujQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.7.1.tgz", + "integrity": "sha512-/HXY0t4FHkpFzjeYS5c16mlA6z0kzn5uKLWptTLTdFSnYpr8FCnOP4Sdkvm2TDQPF2ERxXtNCd+WR/jQugbGnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.7.1.tgz", + "integrity": "sha512-GeW5lVI2GhhnaYckiDzstG2j2Jwlud5d2XefRGwlOK+C/bVGLT1le8MNPYK8wgRlpeK8fG1WnJJYD6Ke7YQ8bg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.7.1.tgz", + "integrity": "sha512-DprxKQkPxIPYwUgg+cscpv2lcIUhn2nxEPlk0UeaiV9vATxCXyytxr1gLcj3xgjGyNPlM0MlJyYaPy1JmRg1cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.7.1.tgz", + "integrity": "sha512-KLlq3kOK7OUyDR757c0zQjPULpGZpLhNB0lZmZpHXvoOUcqZoCXJHh4dT/mryWZJp5ilrem5l8o9ngrDo0X1AA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.7.1.tgz", + "integrity": "sha512-dH7KUjKkSypCeWPiainHyXoES3obS+JIZVoSwSZfKq2gWgs48FY3oT0hQNYrWveE+VR4VoR3b/F3CPGbgFvksA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.7.1.tgz", + "integrity": "sha512-1oeibfyWQPVcijOrTg709qhbXArjX3x1MPjrmA5anlygwrbByxLBcLXvotcOeULFcnH2FYUMMLLant8kgvwE5A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.7.1.tgz", + "integrity": "sha512-D7Q9kDObutuirCNLxYQ7KAg2Xxg99AjcdYz/KuMw5HvyEPbkC9Q7JL0vOrQOrHEHxIQ2lYzFOZvKKoC2yyqXcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@tauri-apps/plugin-dialog": { - "version": "2.2.2", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.3.2.tgz", + "integrity": "sha512-cNLo9YeQSC0MF4IgXnotHsqEgJk72MBZLXmQPrLA95qTaaWiiaFQ38hIMdZ6YbGUNkr3oni3EhU+AD5jLHcdUA==", "license": "MIT OR Apache-2.0", "dependencies": { - "@tauri-apps/api": "^2.0.0" + "@tauri-apps/api": "^2.6.0" } }, "node_modules/@tauri-apps/plugin-global-shortcut": { - "version": "2.2.1", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-global-shortcut/-/plugin-global-shortcut-2.3.0.tgz", + "integrity": "sha512-WbAz0ElhpP+0kzQZRScdCC7UQ7OPH8PAn//fsBNu7+ywihsnVSVOg1L9YhieAtLNtAlnmFI69Yl5AGaA3ge5IQ==", "license": "MIT OR Apache-2.0", "dependencies": { - "@tauri-apps/api": "^2.0.0" + "@tauri-apps/api": "^2.6.0" } }, "node_modules/@tauri-apps/plugin-opener": { - "version": "2.3.0", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.4.0.tgz", + "integrity": "sha512-43VyN8JJtvKWJY72WI/KNZszTpDpzHULFxQs0CJBIYUdCRowQ6Q1feWTDb979N7nldqSuDOaBupZ6wz2nvuWwQ==", "license": "MIT OR Apache-2.0", "dependencies": { - "@tauri-apps/api": "^2.0.0" + "@tauri-apps/api": "^2.6.0" } }, "node_modules/@tauri-apps/plugin-shell": { - "version": "2.2.2", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.0.tgz", + "integrity": "sha512-6GIRxO2z64uxPX4CCTuhQzefvCC0ew7HjdBhMALiGw74vFBDY95VWueAHOHgNOMV4UOUAFupyidN9YulTe5xlA==", "license": "MIT OR Apache-2.0", "dependencies": { - "@tauri-apps/api": "^2.0.0" + "@tauri-apps/api": "^2.6.0" } }, "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": { @@ -1520,6 +3363,8 @@ }, "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": { @@ -1528,6 +3373,8 @@ }, "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": { @@ -1536,27 +3383,37 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.7", + "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.20.7" + "@babel/types": "^7.28.2" } }, "node_modules/@types/d3-array": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", "license": "MIT" }, "node_modules/@types/d3-color": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", "license": "MIT" }, "node_modules/@types/d3-ease": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", "license": "MIT" }, "node_modules/@types/d3-interpolate": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", "license": "MIT", "dependencies": { "@types/d3-color": "*" @@ -1564,10 +3421,14 @@ }, "node_modules/@types/d3-path": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", "license": "MIT" }, "node_modules/@types/d3-scale": { "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", "license": "MIT", "dependencies": { "@types/d3-time": "*" @@ -1575,6 +3436,8 @@ }, "node_modules/@types/d3-shape": { "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", "license": "MIT", "dependencies": { "@types/d3-path": "*" @@ -1582,14 +3445,20 @@ }, "node_modules/@types/d3-time": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", "license": "MIT" }, "node_modules/@types/d3-timer": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, "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": "*" @@ -1597,6 +3466,8 @@ }, "node_modules/@types/diff": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-8.0.0.tgz", + "integrity": "sha512-o7jqJM04gfaYrdCecCVMbZhNdG6T1MHg/oQoRFdERLV+4d+V7FijhiEAbFu0Usww84Yijk9yH58U4Jk4HbtzZw==", "deprecated": "This is a stub types definition. diff provides its own type definitions, so you do not need this installed.", "license": "MIT", "dependencies": { @@ -1604,11 +3475,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", + "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": "*" @@ -1616,6 +3491,8 @@ }, "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": "*" @@ -1623,6 +3500,8 @@ }, "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": "*" @@ -1630,10 +3509,14 @@ }, "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": "22.15.32", + "version": "22.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz", + "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1642,14 +3525,20 @@ }, "node_modules/@types/prismjs": { "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", "license": "MIT" }, "node_modules/@types/prop-types": { "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.23", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", + "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1658,6 +3547,8 @@ }, "node_modules/@types/react-dom": { "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", "peerDependencies": { @@ -1666,6 +3557,8 @@ }, "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": "*" @@ -1673,6 +3566,8 @@ }, "node_modules/@types/sharp": { "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.32.0.tgz", + "integrity": "sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==", "deprecated": "This is a stub types definition. sharp provides its own type definitions, so you do not need this installed.", "dev": true, "license": "MIT", @@ -1682,17 +3577,23 @@ }, "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/@uiw/copy-to-clipboard": { "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.17.tgz", + "integrity": "sha512-O2GUHV90Iw2VrSLVLK0OmNIMdZ5fgEg4NhvtwINsX+eZ/Wf6DWD0TdsK9xwV7dNRnK/UI2mQtl0a2/kRgm1m1A==", "license": "MIT", "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" } }, "node_modules/@uiw/react-markdown-preview": { - "version": "5.1.4", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@uiw/react-markdown-preview/-/react-markdown-preview-5.1.5.tgz", + "integrity": "sha512-DNOqx1a6gJR7Btt57zpGEKTfHRlb7rWbtctMRO2f82wWcuoJsxPBrM+JWebDdOD0LfD8oe2CQvW2ICQJKHQhZg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.17.2", @@ -1717,69 +3618,38 @@ "react-dom": ">=16.8.0" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/react-markdown": { - "version": "9.0.3", + "node_modules/@uiw/react-markdown-preview/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/@uiw/react-markdown-preview/node_modules/hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", "license": "MIT", "dependencies": { - "@types/hast": "^3.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" + "@types/hast": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" - } - }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "hast-util-to-string": "^3.0.0", - "parse-numeric-range": "^1.3.0", - "refractor": "^4.8.0", - "rehype-parse": "^9.0.0", - "unist-util-filter": "^5.0.0", - "unist-util-visit": "^5.0.0" - } - }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor": { - "version": "4.9.0", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^7.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/@types/hast": { + "node_modules/@uiw/react-markdown-preview/node_modules/hast-util-parse-selector/node_modules/@types/hast": { "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "license": "MIT", "dependencies": { "@types/unist": "^2" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/@types/hast/node_modules/@types/unist": { - "version": "2.0.11", - "license": "MIT" - }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/hastscript": { + "node_modules/@uiw/react-markdown-preview/node_modules/hastscript": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", @@ -1793,100 +3663,94 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/hastscript/node_modules/hast-util-parse-selector": { - "version": "3.1.1", + "node_modules/@uiw/react-markdown-preview/node_modules/hastscript/node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "@types/unist": "^2" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/hastscript/node_modules/property-information": { + "node_modules/@uiw/react-markdown-preview/node_modules/property-information": { "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities": { - "version": "4.0.2", + "node_modules/@uiw/react-markdown-preview/node_modules/react-markdown": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.3.tgz", + "integrity": "sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==", "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" + "@types/hast": "^3.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": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "license": "MIT" - }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/character-entities-legacy": { - "version": "3.0.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/character-reference-invalid": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-alphanumerical": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" + "type": "opencollective", + "url": "https://opencollective.com/unified" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-alphanumerical/node_modules/is-alphabetical": { - "version": "2.0.1", + "node_modules/@uiw/react-markdown-preview/node_modules/refractor": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.9.0.tgz", + "integrity": "sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==", "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^7.0.0", + "parse-entities": "^4.0.0" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-decimal": { - "version": "2.0.1", + "node_modules/@uiw/react-markdown-preview/node_modules/refractor/node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "@types/unist": "^2" } }, - "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-hexadecimal": { - "version": "2.0.1", + "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-2.0.0.tgz", + "integrity": "sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "hast-util-to-string": "^3.0.0", + "parse-numeric-range": "^1.3.0", + "refractor": "^4.8.0", + "rehype-parse": "^9.0.0", + "unist-util-filter": "^5.0.0", + "unist-util-visit": "^5.0.0" } }, "node_modules/@uiw/react-md-editor": { - "version": "4.0.7", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@uiw/react-md-editor/-/react-md-editor-4.0.8.tgz", + "integrity": "sha512-S3mOzZeGmJNhzdXJxRTCwsFMDp8nBWeQUf59cK3L6QHzDUHnRoHpcmWpfVRyKGKSg8zaI2+meU5cYWf8kYn3mQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.14.6", @@ -1904,17 +3768,21 @@ }, "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.5.2", + "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.27.4", + "@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.11", + "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, @@ -1922,11 +3790,13 @@ "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/ansi-to-html": { "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", + "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", "license": "MIT", "dependencies": { "entities": "^2.2.0" @@ -1940,6 +3810,8 @@ }, "node_modules/aria-hidden": { "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -1950,6 +3822,8 @@ }, "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", @@ -1958,6 +3832,8 @@ }, "node_modules/base64-arraybuffer": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -1965,6 +3841,8 @@ }, "node_modules/bcp-47-match": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", "license": "MIT", "funding": { "type": "github", @@ -1973,10 +3851,14 @@ }, "node_modules/boolbase": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, "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==", "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1986,7 +3868,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.0", + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", + "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", "dev": true, "funding": [ { @@ -2004,8 +3888,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", + "caniuse-lite": "^1.0.30001733", + "electron-to-chromium": "^1.5.199", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -2017,7 +3901,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001723", + "version": "1.0.30001734", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz", + "integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==", "dev": true, "funding": [ { @@ -2037,6 +3923,8 @@ }, "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", @@ -2044,7 +3932,9 @@ } }, "node_modules/character-entities": { - "version": "1.2.4", + "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", @@ -2053,6 +3943,8 @@ }, "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", @@ -2060,7 +3952,9 @@ } }, "node_modules/character-entities-legacy": { - "version": "1.1.4", + "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", @@ -2068,7 +3962,9 @@ } }, "node_modules/character-reference-invalid": { - "version": "1.1.4", + "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", @@ -2077,6 +3973,8 @@ }, "node_modules/chownr": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -2084,6 +3982,8 @@ }, "node_modules/class-variance-authority": { "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", "license": "Apache-2.0", "dependencies": { "clsx": "^2.1.1" @@ -2094,6 +3994,8 @@ }, "node_modules/clsx": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { "node": ">=6" @@ -2101,6 +4003,8 @@ }, "node_modules/color": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "dev": true, "license": "MIT", "dependencies": { @@ -2113,6 +4017,8 @@ }, "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": { @@ -2124,11 +4030,15 @@ }, "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/color-string": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -2138,6 +4048,8 @@ }, "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", @@ -2146,18 +4058,35 @@ }, "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/core-js": { + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.0.tgz", + "integrity": "sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/css-line-break": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", "license": "MIT", "dependencies": { "utrie": "^1.0.2" } }, "node_modules/css-selector-parser": { - "version": "3.1.2", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.1.3.tgz", + "integrity": "sha512-gJMigczVZqYAk0hPVzx/M4Hm1D9QOtqkdQk9005TNzDIUGzo5cnHEDiKUT7jGPximL/oYb+LIitcHFQ4aKupxg==", "funding": [ { "type": "github", @@ -2172,10 +4101,14 @@ }, "node_modules/csstype": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, "node_modules/d3-array": { "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", "license": "ISC", "dependencies": { "internmap": "1 - 2" @@ -2186,6 +4119,8 @@ }, "node_modules/d3-color": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", "license": "ISC", "engines": { "node": ">=12" @@ -2193,6 +4128,8 @@ }, "node_modules/d3-ease": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", "license": "BSD-3-Clause", "engines": { "node": ">=12" @@ -2200,6 +4137,8 @@ }, "node_modules/d3-format": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", "license": "ISC", "engines": { "node": ">=12" @@ -2207,6 +4146,8 @@ }, "node_modules/d3-interpolate": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "license": "ISC", "dependencies": { "d3-color": "1 - 3" @@ -2217,6 +4158,8 @@ }, "node_modules/d3-path": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", "license": "ISC", "engines": { "node": ">=12" @@ -2224,6 +4167,8 @@ }, "node_modules/d3-scale": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "license": "ISC", "dependencies": { "d3-array": "2.10.0 - 3", @@ -2238,6 +4183,8 @@ }, "node_modules/d3-shape": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "license": "ISC", "dependencies": { "d3-path": "^3.1.0" @@ -2248,6 +4195,8 @@ }, "node_modules/d3-time": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "license": "ISC", "dependencies": { "d3-array": "2 - 3" @@ -2258,6 +4207,8 @@ }, "node_modules/d3-time-format": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "license": "ISC", "dependencies": { "d3-time": "1 - 3" @@ -2268,6 +4219,8 @@ }, "node_modules/d3-timer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", "license": "ISC", "engines": { "node": ">=12" @@ -2275,6 +4228,8 @@ }, "node_modules/date-fns": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "license": "MIT", "funding": { "type": "github", @@ -2283,6 +4238,8 @@ }, "node_modules/debug": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2298,10 +4255,14 @@ }, "node_modules/decimal.js-light": { "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, "node_modules/decode-named-character-reference": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", "license": "MIT", "dependencies": { "character-entities": "^2.0.0" @@ -2311,34 +4272,37 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/decode-named-character-reference/node_modules/character-entities": { - "version": "2.0.2", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "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-libc": { - "version": "2.0.4", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, "node_modules/detect-node-es": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, "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" @@ -2350,6 +4314,8 @@ }, "node_modules/diff": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -2357,6 +4323,8 @@ }, "node_modules/direction": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", "license": "MIT", "bin": { "direction": "cli.js" @@ -2368,6 +4336,8 @@ }, "node_modules/dom-helpers": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.7", @@ -2375,12 +4345,16 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.169", + "version": "1.5.200", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.200.tgz", + "integrity": "sha512-rFCxROw7aOe4uPTfIAx+rXv9cEcGx+buAF4npnhtTqCJk5KDFRnh3+KYj7rdVh6lsFt5/aPs+Irj9rZ33WMA7w==", "dev": true, "license": "ISC" }, "node_modules/enhanced-resolve": { - "version": "5.18.1", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2392,13 +4366,17 @@ }, "node_modules/entities": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "license": "BSD-2-Clause", "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/esbuild": { - "version": "0.25.5", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -2408,35 +4386,38 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "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": { @@ -2445,6 +4426,8 @@ }, "node_modules/escape-string-regexp": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { "node": ">=12" @@ -2455,6 +4438,8 @@ }, "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", @@ -2463,14 +4448,20 @@ }, "node_modules/eventemitter3": { "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, "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-equals": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2478,6 +4469,8 @@ }, "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" @@ -2487,20 +4480,16 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/fdir": { - "version": "6.4.6", - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "license": "MIT" }, "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==", "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -2511,16 +4500,20 @@ }, "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/framer-motion": { - "version": "12.18.1", + "version": "12.23.12", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz", + "integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==", "license": "MIT", "dependencies": { - "motion-dom": "^12.18.1", - "motion-utils": "^12.18.1", + "motion-dom": "^12.23.12", + "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { @@ -2542,6 +4535,9 @@ }, "node_modules/fsevents": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -2553,6 +4549,8 @@ }, "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": { @@ -2561,6 +4559,8 @@ }, "node_modules/get-nonce": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", "engines": { "node": ">=6" @@ -2568,22 +4568,20 @@ }, "node_modules/github-slugger": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", "license": "ISC" }, - "node_modules/globals": { - "version": "11.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "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==", "license": "ISC" }, "node_modules/hast-util-from-html": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -2600,6 +4598,8 @@ }, "node_modules/hast-util-from-parse5": { "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -2616,26 +4616,30 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-parse5/node_modules/hastscript": { - "version": "9.0.1", + "node_modules/hast-util-from-parse5/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", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" + "@types/hast": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-parse5/node_modules/hastscript/node_modules/hast-util-parse-selector": { - "version": "4.0.0", + "node_modules/hast-util-from-parse5/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" + "@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", @@ -2644,6 +4648,8 @@ }, "node_modules/hast-util-has-property": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -2655,6 +4661,8 @@ }, "node_modules/hast-util-heading-rank": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz", + "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -2666,6 +4674,8 @@ }, "node_modules/hast-util-is-element": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -2677,6 +4687,8 @@ }, "node_modules/hast-util-parse-selector": { "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -2685,6 +4697,8 @@ }, "node_modules/hast-util-raw": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -2706,8 +4720,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-sanitize": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", + "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "unist-util-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-select": { "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -2733,6 +4764,8 @@ }, "node_modules/hast-util-to-html": { "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -2754,6 +4787,8 @@ }, "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", @@ -2779,6 +4814,8 @@ }, "node_modules/hast-util-to-parse5": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -2796,6 +4833,8 @@ }, "node_modules/hast-util-to-parse5/node_modules/property-information": { "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "license": "MIT", "funding": { "type": "github", @@ -2804,6 +4843,8 @@ }, "node_modules/hast-util-to-string": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -2815,6 +4856,8 @@ }, "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" @@ -2826,6 +4869,8 @@ }, "node_modules/hastscript": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", @@ -2841,17 +4886,23 @@ }, "node_modules/hastscript/node_modules/@types/hast": { "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "license": "MIT", "dependencies": { "@types/unist": "^2" } }, - "node_modules/hastscript/node_modules/@types/hast/node_modules/@types/unist": { + "node_modules/hastscript/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/hastscript/node_modules/comma-separated-tokens": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", "license": "MIT", "funding": { "type": "github", @@ -2860,6 +4911,8 @@ }, "node_modules/hastscript/node_modules/property-information": { "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "license": "MIT", "dependencies": { "xtend": "^4.0.0" @@ -2871,6 +4924,8 @@ }, "node_modules/hastscript/node_modules/space-separated-tokens": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", "license": "MIT", "funding": { "type": "github", @@ -2879,6 +4934,8 @@ }, "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": "*" @@ -2886,10 +4943,14 @@ }, "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-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", @@ -2898,6 +4959,8 @@ }, "node_modules/html-void-elements": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", "license": "MIT", "funding": { "type": "github", @@ -2906,6 +4969,8 @@ }, "node_modules/html2canvas": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", "license": "MIT", "dependencies": { "css-line-break": "^2.1.0", @@ -2917,17 +4982,23 @@ }, "node_modules/inline-style-parser": { "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, "node_modules/internmap": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/is-alphabetical": { - "version": "1.0.4", + "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", @@ -2935,11 +5006,13 @@ } }, "node_modules/is-alphanumerical": { - "version": "1.0.4", + "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": "^1.0.0", - "is-decimal": "^1.0.0" + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" }, "funding": { "type": "github", @@ -2948,11 +5021,15 @@ }, "node_modules/is-arrayish": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "dev": true, "license": "MIT" }, "node_modules/is-decimal": { - "version": "1.0.4", + "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", @@ -2961,6 +5038,8 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2968,6 +5047,8 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -2977,7 +5058,9 @@ } }, "node_modules/is-hexadecimal": { - "version": "1.0.4", + "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", @@ -2986,6 +5069,8 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -2993,6 +5078,8 @@ }, "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" @@ -3002,7 +5089,9 @@ } }, "node_modules/jiti": { - "version": "2.4.2", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -3010,10 +5099,14 @@ }, "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==", "license": "MIT" }, "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": { @@ -3025,50 +5118,236 @@ }, "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": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss": { + "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" } }, - "node_modules/lightningcss-darwin-arm64": { + "node_modules/lightningcss-win32-x64-msvc": { "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", "cpu": [ - "arm64" + "x64" ], "license": "MPL-2.0", "optional": true, "os": [ - "darwin" + "win32" ], "engines": { "node": ">= 12.0.0" @@ -3078,12 +5357,25 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/lodash": { "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "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", @@ -3092,6 +5384,8 @@ }, "node_modules/loose-envify": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -3102,6 +5396,8 @@ }, "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", @@ -3114,19 +5410,18 @@ }, "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/lru-cache/node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, "node_modules/lucide-react": { "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", + "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" @@ -3134,6 +5429,8 @@ }, "node_modules/magic-string": { "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -3141,6 +5438,8 @@ }, "node_modules/markdown-table": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", "license": "MIT", "funding": { "type": "github", @@ -3149,6 +5448,8 @@ }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3163,6 +5464,8 @@ }, "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", @@ -3185,6 +5488,8 @@ }, "node_modules/mdast-util-gfm": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "license": "MIT", "dependencies": { "mdast-util-from-markdown": "^2.0.0", @@ -3202,6 +5507,8 @@ }, "node_modules/mdast-util-gfm-autolink-literal": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3217,6 +5524,8 @@ }, "node_modules/mdast-util-gfm-footnote": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3232,6 +5541,8 @@ }, "node_modules/mdast-util-gfm-strikethrough": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3245,6 +5556,8 @@ }, "node_modules/mdast-util-gfm-table": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3260,6 +5573,8 @@ }, "node_modules/mdast-util-gfm-task-list-item": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3274,6 +5589,8 @@ }, "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", @@ -3290,6 +5607,8 @@ }, "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", @@ -3310,81 +5629,10 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities": { - "version": "4.0.2", - "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/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "license": "MIT" - }, - "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/character-entities-legacy": { - "version": "3.0.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/character-reference-invalid": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/is-alphanumerical": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/is-alphanumerical/node_modules/is-alphabetical": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/is-decimal": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/is-hexadecimal": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "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", @@ -3401,6 +5649,8 @@ }, "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", @@ -3413,6 +5663,8 @@ }, "node_modules/mdast-util-to-hast": { "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -3432,6 +5684,8 @@ }, "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", @@ -3451,6 +5705,8 @@ }, "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" @@ -3462,6 +5718,8 @@ }, "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", @@ -3495,6 +5753,8 @@ }, "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", @@ -3527,6 +5787,8 @@ }, "node_modules/micromark-extension-gfm": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", "license": "MIT", "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", @@ -3545,6 +5807,8 @@ }, "node_modules/micromark-extension-gfm-autolink-literal": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", "license": "MIT", "dependencies": { "micromark-util-character": "^2.0.0", @@ -3559,6 +5823,8 @@ }, "node_modules/micromark-extension-gfm-footnote": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3577,6 +5843,8 @@ }, "node_modules/micromark-extension-gfm-strikethrough": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3593,6 +5861,8 @@ }, "node_modules/micromark-extension-gfm-table": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3608,6 +5878,8 @@ }, "node_modules/micromark-extension-gfm-tagfilter": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", "license": "MIT", "dependencies": { "micromark-util-types": "^2.0.0" @@ -3619,6 +5891,8 @@ }, "node_modules/micromark-extension-gfm-task-list-item": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3634,6 +5908,8 @@ }, "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", @@ -3653,6 +5929,8 @@ }, "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", @@ -3673,6 +5951,8 @@ }, "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", @@ -3691,6 +5971,8 @@ }, "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", @@ -3711,6 +5993,8 @@ }, "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", @@ -3731,6 +6015,8 @@ }, "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", @@ -3749,6 +6035,8 @@ }, "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", @@ -3766,6 +6054,8 @@ }, "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", @@ -3785,6 +6075,8 @@ }, "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", @@ -3803,6 +6095,8 @@ }, "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", @@ -3820,6 +6114,8 @@ }, "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", @@ -3840,6 +6136,8 @@ }, "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", @@ -3854,6 +6152,8 @@ }, "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", @@ -3868,6 +6168,8 @@ }, "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", @@ -3885,6 +6187,8 @@ }, "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", @@ -3902,6 +6206,8 @@ }, "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", @@ -3921,6 +6227,8 @@ }, "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", @@ -3941,6 +6249,8 @@ }, "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", @@ -3955,6 +6265,8 @@ }, "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", @@ -3969,6 +6281,8 @@ }, "node_modules/micromatch": { "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -3978,18 +6292,10 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/minipass": { "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -3997,6 +6303,8 @@ }, "node_modules/minizlib": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -4007,6 +6315,8 @@ }, "node_modules/mkdirp": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "license": "MIT", "bin": { "mkdirp": "dist/cjs/src/bin.js" @@ -4019,18 +6329,24 @@ } }, "node_modules/motion-dom": { - "version": "12.18.1", + "version": "12.23.12", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.12.tgz", + "integrity": "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==", "license": "MIT", "dependencies": { - "motion-utils": "^12.18.1" + "motion-utils": "^12.23.6" } }, "node_modules/motion-utils": { - "version": "12.18.1", + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", "license": "MIT" }, "node_modules/mri": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "license": "MIT", "engines": { "node": ">=4" @@ -4038,19 +6354,45 @@ }, "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==", + "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/node-addon-api": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "license": "MIT" }, "node_modules/node-releases": { "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, "node_modules/nth-check": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" @@ -4061,33 +6403,48 @@ }, "node_modules/object-assign": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/parse-entities": { - "version": "2.0.0", + "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": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" + "@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-numeric-range": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", "license": "ISC" }, "node_modules/parse5": { "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -4098,6 +6455,8 @@ }, "node_modules/parse5/node_modules/entities": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -4108,13 +6467,17 @@ }, "node_modules/picocolors": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -4122,6 +6485,8 @@ }, "node_modules/postcss": { "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -4146,24 +6511,44 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.11", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "node_modules/posthog-js": { + "version": "1.259.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.259.0.tgz", + "integrity": "sha512-6usLnJshky8fQ82ask7PIJh4BSFOU0VkRbFg8Zanm/HIlYMG1VOdRWlToA63JXeO7Bzm9TuREq1wFm5U2VEVCg==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "core-js": "^3.38.1", + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@rrweb/types": "2.0.0-alpha.17", + "rrweb-snapshot": "2.0.0-alpha.17" + }, + "peerDependenciesMeta": { + "@rrweb/types": { + "optional": true + }, + "rrweb-snapshot": { + "optional": true } - ], + } + }, + "node_modules/preact": { + "version": "10.27.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.0.tgz", + "integrity": "sha512-/DTYoB6mwwgPytiqQTh/7SFRL98ZdiD8Sk8zIUVOxtwq4oWcwrcd1uno9fE/zZmUaUrFNYzbH14CPebOz9tZQw==", "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" } }, "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" @@ -4171,6 +6556,8 @@ }, "node_modules/prop-types": { "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -4180,10 +6567,14 @@ }, "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, "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", @@ -4192,6 +6583,8 @@ }, "node_modules/react": { "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -4202,6 +6595,8 @@ }, "node_modules/react-dom": { "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", @@ -4212,7 +6607,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.58.1", + "version": "7.62.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", + "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -4227,10 +6624,14 @@ }, "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==", "license": "MIT" }, "node_modules/react-markdown": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", + "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4256,6 +6657,8 @@ }, "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": { @@ -4264,6 +6667,8 @@ }, "node_modules/react-remove-scroll": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "license": "MIT", "dependencies": { "react-remove-scroll-bar": "^2.3.7", @@ -4287,6 +6692,8 @@ }, "node_modules/react-remove-scroll-bar": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", "dependencies": { "react-style-singleton": "^2.2.2", @@ -4307,6 +6714,8 @@ }, "node_modules/react-smooth": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", "license": "MIT", "dependencies": { "fast-equals": "^5.0.1", @@ -4320,6 +6729,8 @@ }, "node_modules/react-style-singleton": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", "dependencies": { "get-nonce": "^1.0.0", @@ -4340,6 +6751,8 @@ }, "node_modules/react-syntax-highlighter": { "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz", + "integrity": "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", @@ -4353,55 +6766,155 @@ "react": ">= 0.14.0" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "license": "BSD-3-Clause", + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "license": "MIT", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/recharts": { - "version": "2.15.3", + "node_modules/refractor/node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/recharts-scale": { - "version": "0.4.5", + "node_modules/refractor/node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/refractor": { - "version": "3.6.0", + "node_modules/refractor/node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "license": "MIT", "dependencies": { - "hastscript": "^6.0.0", - "parse-entities": "^2.0.0", - "prismjs": "~1.27.0" + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" }, "funding": { "type": "github", @@ -4410,6 +6923,8 @@ }, "node_modules/refractor/node_modules/prismjs": { "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", "license": "MIT", "engines": { "node": ">=6" @@ -4417,6 +6932,8 @@ }, "node_modules/rehype": { "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4431,6 +6948,8 @@ }, "node_modules/rehype-attr": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/rehype-attr/-/rehype-attr-3.0.3.tgz", + "integrity": "sha512-Up50Xfra8tyxnkJdCzLBIBtxOcB2M1xdeKe1324U06RAvSjYm7ULSeoM+b/nYPQPVd7jsXJ9+39IG1WAJPXONw==", "license": "MIT", "dependencies": { "unified": "~11.0.0", @@ -4445,6 +6964,8 @@ }, "node_modules/rehype-autolink-headings": { "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-7.1.0.tgz", + "integrity": "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4461,6 +6982,8 @@ }, "node_modules/rehype-ignore": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rehype-ignore/-/rehype-ignore-2.0.2.tgz", + "integrity": "sha512-BpAT/3lU9DMJ2siYVD/dSR0A/zQgD6Fb+fxkJd4j+wDVy6TYbYpK+FZqu8eM9EuNKGvi4BJR7XTZ/+zF02Dq8w==", "license": "MIT", "dependencies": { "hast-util-select": "^6.0.0", @@ -4476,6 +6999,8 @@ }, "node_modules/rehype-parse": { "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4489,6 +7014,8 @@ }, "node_modules/rehype-prism-plus": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-2.0.1.tgz", + "integrity": "sha512-Wglct0OW12tksTUseAPyWPo3srjBOY7xKlql/DPKi7HbsdZTyaLCAoO58QBKSczFQxElTsQlOY3JDOFzB/K++Q==", "license": "MIT", "dependencies": { "hast-util-to-string": "^3.0.0", @@ -4499,140 +7026,81 @@ "unist-util-visit": "^5.0.0" } }, - "node_modules/rehype-prism-plus/node_modules/refractor": { - "version": "4.9.0", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^7.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/@types/hast": { + "node_modules/rehype-prism-plus/node_modules/@types/hast": { "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "license": "MIT", "dependencies": { "@types/unist": "^2" } }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/@types/hast/node_modules/@types/unist": { + "node_modules/rehype-prism-plus/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/rehype-prism-plus/node_modules/refractor/node_modules/hastscript": { - "version": "7.2.0", + "node_modules/rehype-prism-plus/node_modules/hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^3.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" + "@types/hast": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/hastscript/node_modules/hast-util-parse-selector": { - "version": "3.1.1", + "node_modules/rehype-prism-plus/node_modules/hastscript": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0" + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/hastscript/node_modules/property-information": { + "node_modules/rehype-prism-plus/node_modules/property-information": { "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities": { - "version": "4.0.2", - "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/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "license": "MIT" - }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/character-entities-legacy": { - "version": "3.0.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/character-reference-invalid": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-alphanumerical": { - "version": "2.0.1", + "node_modules/rehype-prism-plus/node_modules/refractor": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.9.0.tgz", + "integrity": "sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==", "license": "MIT", "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" + "@types/hast": "^2.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^7.0.0", + "parse-entities": "^4.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-alphanumerical/node_modules/is-alphabetical": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-decimal": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/rehype-prism-plus/node_modules/refractor/node_modules/parse-entities/node_modules/is-hexadecimal": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/rehype-raw": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4646,6 +7114,8 @@ }, "node_modules/rehype-rewrite": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rehype-rewrite/-/rehype-rewrite-4.0.2.tgz", + "integrity": "sha512-rjLJ3z6fIV11phwCqHp/KRo8xuUCO8o9bFJCNw5o6O2wlLk6g8r323aRswdGBQwfXPFYeSuZdAjp4tzo6RGqEg==", "license": "MIT", "dependencies": { "hast-util-select": "^6.0.0", @@ -4659,8 +7129,24 @@ "url": "https://jaywcjlove.github.io/#/sponsor" } }, + "node_modules/rehype-sanitize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", + "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-sanitize": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/rehype-slug": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz", + "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4676,6 +7162,8 @@ }, "node_modules/rehype-stringify": { "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4689,6 +7177,8 @@ }, "node_modules/remark-gfm": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4705,6 +7195,8 @@ }, "node_modules/remark-github-blockquote-alert": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/remark-github-blockquote-alert/-/remark-github-blockquote-alert-1.3.1.tgz", + "integrity": "sha512-OPNnimcKeozWN1w8KVQEuHOxgN3L4rah8geMOLhA5vN9wITqU4FWD+G26tkEsCGHiOVDbISx+Se5rGZ+D1p0Jg==", "license": "MIT", "dependencies": { "unist-util-visit": "^5.0.0" @@ -4718,6 +7210,8 @@ }, "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", @@ -4732,6 +7226,8 @@ }, "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", @@ -4747,6 +7243,8 @@ }, "node_modules/remark-stringify": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4759,10 +7257,12 @@ } }, "node_modules/rollup": { - "version": "4.43.0", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -4772,49 +7272,52 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.43.0", - "@rollup/rollup-android-arm64": "4.43.0", - "@rollup/rollup-darwin-arm64": "4.43.0", - "@rollup/rollup-darwin-x64": "4.43.0", - "@rollup/rollup-freebsd-arm64": "4.43.0", - "@rollup/rollup-freebsd-x64": "4.43.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", - "@rollup/rollup-linux-arm-musleabihf": "4.43.0", - "@rollup/rollup-linux-arm64-gnu": "4.43.0", - "@rollup/rollup-linux-arm64-musl": "4.43.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-musl": "4.43.0", - "@rollup/rollup-linux-s390x-gnu": "4.43.0", - "@rollup/rollup-linux-x64-gnu": "4.43.0", - "@rollup/rollup-linux-x64-musl": "4.43.0", - "@rollup/rollup-win32-arm64-msvc": "4.43.0", - "@rollup/rollup-win32-ia32-msvc": "4.43.0", - "@rollup/rollup-win32-x64-msvc": "4.43.0", + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" } }, "node_modules/scheduler": { "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } }, "node_modules/semver": { - "version": "7.7.2", + "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" - }, - "engines": { - "node": ">=10" } }, "node_modules/sharp": { - "version": "0.34.2", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -4830,52 +7333,57 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" - } - }, - "node_modules/sharp/node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "cpu": [ - "arm64" - ], + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" + } + }, + "node_modules/sharp/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "dev": true, "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">=8" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "engines": { + "node": ">=10" } }, "node_modules/simple-swizzle": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "dev": true, "license": "MIT", "dependencies": { @@ -4884,6 +7392,8 @@ }, "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==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -4891,6 +7401,8 @@ }, "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", @@ -4899,6 +7411,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", @@ -4909,16 +7423,10 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/stringify-entities/node_modules/character-entities-legacy": { - "version": "3.0.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/style-to-js": { "version": "1.1.17", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", + "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", "license": "MIT", "dependencies": { "style-to-object": "1.0.9" @@ -4926,6 +7434,8 @@ }, "node_modules/style-to-object": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", + "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", "license": "MIT", "dependencies": { "inline-style-parser": "0.2.4" @@ -4933,6 +7443,8 @@ }, "node_modules/tailwind-merge": { "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", "license": "MIT", "funding": { "type": "github", @@ -4940,11 +7452,15 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.10", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", "license": "MIT" }, "node_modules/tapable": { "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "license": "MIT", "engines": { "node": ">=6" @@ -4952,6 +7468,8 @@ }, "node_modules/tar": { "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -4965,8 +7483,19 @@ "node": ">=18" } }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", "license": "MIT", "dependencies": { "utrie": "^1.0.2" @@ -4974,10 +7503,14 @@ }, "node_modules/tiny-invariant": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, "node_modules/tinyglobby": { "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "license": "MIT", "dependencies": { "fdir": "^6.4.4", @@ -4990,8 +7523,36 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "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==", "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -5002,6 +7563,8 @@ }, "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", @@ -5010,6 +7573,8 @@ }, "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", @@ -5018,10 +7583,14 @@ }, "node_modules/tslib": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/typescript": { "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5034,11 +7603,15 @@ }, "node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "devOptional": 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", @@ -5056,6 +7629,8 @@ }, "node_modules/unist-util-filter": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-5.0.1.tgz", + "integrity": "sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -5065,6 +7640,8 @@ }, "node_modules/unist-util-is": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -5076,6 +7653,8 @@ }, "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" @@ -5087,6 +7666,8 @@ }, "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" @@ -5098,6 +7679,8 @@ }, "node_modules/unist-util-visit": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -5111,6 +7694,8 @@ }, "node_modules/unist-util-visit-parents": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -5123,6 +7708,8 @@ }, "node_modules/update-browserslist-db": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -5152,6 +7739,8 @@ }, "node_modules/use-callback-ref": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -5171,6 +7760,8 @@ }, "node_modules/use-sidecar": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "license": "MIT", "dependencies": { "detect-node-es": "^1.1.0", @@ -5191,6 +7782,8 @@ }, "node_modules/utrie": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", "license": "MIT", "dependencies": { "base64-arraybuffer": "^1.0.2" @@ -5198,6 +7791,8 @@ }, "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", @@ -5210,6 +7805,8 @@ }, "node_modules/vfile-location": { "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -5221,7 +7818,9 @@ } }, "node_modules/vfile-message": { - "version": "4.0.2", + "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", @@ -5234,6 +7833,8 @@ }, "node_modules/victory-vendor": { "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", "license": "MIT AND ISC", "dependencies": { "@types/d3-array": "^3.0.3", @@ -5254,6 +7855,8 @@ }, "node_modules/vite": { "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -5324,39 +7927,77 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/web-namespaces": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, "node_modules/xtend": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "license": "MIT", "engines": { "node": ">=0.4" } }, "node_modules/yallist": { - "version": "5.0.0", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } + "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/zod": { - "version": "3.25.67", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zustand": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.6.tgz", - "integrity": "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.7.tgz", + "integrity": "sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==", "license": "MIT", "engines": { "node": ">=12.20.0" @@ -5384,6 +8025,8 @@ }, "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", diff --git a/package.json b/package.json index 42b001a6e..97d6a3cc2 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "date-fns": "^3.6.0", "diff": "^8.0.2", "framer-motion": "^12.0.0-alpha.1", + "hast-util-sanitize": "^5.0.2", "html2canvas": "^1.4.1", "lucide-react": "^0.468.0", "posthog-js": "^1.258.3", @@ -50,6 +51,8 @@ "react-markdown": "^9.0.3", "react-syntax-highlighter": "^15.6.1", "recharts": "^2.14.1", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.0", "tailwind-merge": "^2.6.0", "tailwindcss": "^4.1.8", diff --git a/reg-think.jpg b/reg-think.jpg new file mode 100644 index 000000000..0409c4ea2 Binary files /dev/null and b/reg-think.jpg differ diff --git a/seq-think.jpg b/seq-think.jpg new file mode 100644 index 000000000..83c841306 Binary files /dev/null and b/seq-think.jpg differ diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 5e231dacf..867b6877e 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -52,7 +52,7 @@ "updater:default", "core:window:allow-minimize", "core:window:allow-maximize", - "core:window:allow-unmaximize", + "core:window:allow-unmaximize", "core:window:allow-close", "core:window:allow-is-maximized", "core:window:allow-start-dragging" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 56a8e77de..3ae9d308c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -28,17 +28,13 @@ "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval' https://app.posthog.com https://*.posthog.com https://*.i.posthog.com https://*.assets.i.posthog.com; connect-src 'self' ipc: https://ipc.localhost https://app.posthog.com https://*.posthog.com https://*.i.posthog.com", "assetProtocol": { "enable": true, - "scope": [ - "**" - ] + "scope": ["**"] } } }, "plugins": { "fs": { - "scope": [ - "$HOME/**" - ], + "scope": ["$HOME/**"], "allow": [ "readFile", "writeFile", @@ -57,13 +53,7 @@ }, "bundle": { "active": true, - "targets": [ - "deb", - "rpm", - "appimage", - "app", - "dmg" - ], + "targets": ["deb", "rpm", "appimage", "app", "dmg"], "icon": [ "icons/32x32.png", "icons/64x64.png", diff --git a/src-tauri/tests/TESTS_COMPLETE.md b/src-tauri/tests/TESTS_COMPLETE.md index 4276a668b..3cc71d93a 100644 --- a/src-tauri/tests/TESTS_COMPLETE.md +++ b/src-tauri/tests/TESTS_COMPLETE.md @@ -5,16 +5,19 @@ ### Key Changes from Original Task: 1. **Replaced MockClaude with Real Claude Execution** ✅ + - Removed all mock Claude implementations - Tests now execute actual `claude` command with `--dangerously-skip-permissions` - Added proper timeout handling for macOS/Linux compatibility 2. **Real Claude Test Implementation** ✅ + - Created `claude_real.rs` with helper functions for executing real Claude - Tests use actual Claude CLI with test prompts - Proper handling of stdout/stderr/exit codes 3. **Test Suite Results:** + ``` test result: ok. 58 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` @@ -22,18 +25,22 @@ test result: ok. 58 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ### Implementation Details: #### Real Claude Execution: + - `execute_claude_task()` - Executes Claude with specified task and captures output - Supports timeout handling (gtimeout on macOS, timeout on Linux) - Returns structured output with stdout, stderr, exit code, and duration - Helper methods for checking operation results #### Test Tasks: + - Simple, focused prompts that execute quickly - Example: "Read the file ./test.txt in the current directory and show its contents" - 20-second timeout to allow Claude sufficient time to respond #### Key Test Updates: + 1. **Agent Tests**: + - Test agent execution with various permission configurations - Test agent execution in different project contexts - Control tests for baseline behavior @@ -43,12 +50,14 @@ test result: ok. 58 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - Test Claude execution with custom configurations ### Benefits of Real Claude Testing: + - **Authenticity**: Tests validate actual Claude behavior, not mocked responses - **Integration**: Ensures the system works with real Claude execution - **End-to-End**: Complete validation from command invocation to output parsing - **No External Dependencies**: Uses `--dangerously-skip-permissions` flag ### Notes: + - All tests use real Claude CLI commands - No ignored tests - No TODOs in test code diff --git a/src/assets/shimmer.css b/src/assets/shimmer.css index 27e0a2328..033db9771 100644 --- a/src/assets/shimmer.css +++ b/src/assets/shimmer.css @@ -45,7 +45,8 @@ 95% { background-position: 200% center; } - 96%, 100% { + 96%, + 100% { background-position: 200% center; -webkit-text-fill-color: currentColor; background: none; @@ -66,27 +67,27 @@ @keyframes symbol-rotate { 0% { - content: '◐'; + content: "◐"; opacity: 1; transform: translateY(0) scale(1); } 25% { - content: '◓'; + content: "◓"; opacity: 1; transform: translateY(0) scale(1); } 50% { - content: '◑'; + content: "◑"; opacity: 1; transform: translateY(0) scale(1); } 75% { - content: '◒'; + content: "◒"; opacity: 1; transform: translateY(0) scale(1); } 100% { - content: '◐'; + content: "◐"; opacity: 1; transform: translateY(0) scale(1); } @@ -156,7 +157,7 @@ } .rotating-symbol::before { - content: '◐'; + content: "◐"; display: inline-block; animation: symbol-rotate 2s linear infinite; font-size: inherit; @@ -175,7 +176,7 @@ } .shimmer-hover::before { - content: ''; + content: ""; position: absolute; top: -50%; left: 0; @@ -202,4 +203,4 @@ .shimmer-hover:hover::before { animation: shimmer 1s ease-out; -} +} diff --git a/src/components/AgentExecution.tsx b/src/components/AgentExecution.tsx index 41fbb1fd7..c16a0ac97 100644 --- a/src/components/AgentExecution.tsx +++ b/src/components/AgentExecution.tsx @@ -1,9 +1,9 @@ import React, { useState, useEffect, useRef } from "react"; import { motion, AnimatePresence } from "framer-motion"; -import { - ArrowLeft, - Play, - StopCircle, +import { + ArrowLeft, + Play, + StopCircle, Terminal, AlertCircle, Loader2, @@ -11,7 +11,7 @@ import { ChevronDown, Maximize2, X, - Settings2 + Settings2, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -32,7 +32,11 @@ import { ExecutionControlBar } from "./ExecutionControlBar"; import { ErrorBoundary } from "./ErrorBoundary"; import { useVirtualizer } from "@tanstack/react-virtual"; import { HooksEditor } from "./HooksEditor"; -import { useTrackEvent, useComponentMetrics, useFeatureAdoptionTracking } from "@/hooks"; +import { + useTrackEvent, + useComponentMetrics, + useFeatureAdoptionTracking, +} from "@/hooks"; import { useTabState } from "@/hooks/useTabState"; interface AgentExecutionProps { @@ -77,7 +81,7 @@ export interface ClaudeStreamMessage { /** * AgentExecution component for running CC agents - * + * * @example * setView('list')} /> */ @@ -92,30 +96,34 @@ export const AgentExecution: React.FC = ({ const [task, setTask] = useState(agent.default_task || ""); const [model, setModel] = useState(agent.model || "sonnet"); const [isRunning, setIsRunning] = useState(false); - + // Get tab state functions const { updateTabStatus } = useTabState(); const [messages, setMessages] = useState([]); const [rawJsonlOutput, setRawJsonlOutput] = useState([]); const [error, setError] = useState(null); const [copyPopoverOpen, setCopyPopoverOpen] = useState(false); - + // Analytics tracking const trackEvent = useTrackEvent(); - useComponentMetrics('AgentExecution'); - const agentFeatureTracking = useFeatureAdoptionTracking(`agent_${agent.name || 'custom'}`); - + useComponentMetrics("AgentExecution"); + const agentFeatureTracking = useFeatureAdoptionTracking( + `agent_${agent.name || "custom"}`, + ); + // Hooks configuration state const [isHooksDialogOpen, setIsHooksDialogOpen] = useState(false); const [activeHooksTab, setActiveHooksTab] = useState("project"); // Execution stats - const [executionStartTime, setExecutionStartTime] = useState(null); + const [executionStartTime, setExecutionStartTime] = useState( + null, + ); const [totalTokens, setTotalTokens] = useState(0); const [elapsedTime, setElapsedTime] = useState(0); const [hasUserScrolled, setHasUserScrolled] = useState(false); const [isFullscreenModalOpen, setIsFullscreenModalOpen] = useState(false); - + const messagesEndRef = useRef(null); const messagesContainerRef = useRef(null); const scrollContainerRef = useRef(null); @@ -136,12 +144,15 @@ export const AgentExecution: React.FC = ({ // Skip empty user messages if (message.type === "user" && message.message) { if (message.isMeta) return false; - + const msg = message.message; - if (!msg.content || (Array.isArray(msg.content) && msg.content.length === 0)) { + if ( + !msg.content || + (Array.isArray(msg.content) && msg.content.length === 0) + ) { return false; } - + // Check if user message has visible content by checking its parts if (Array.isArray(msg.content)) { let hasVisibleContent = false; @@ -156,17 +167,33 @@ export const AgentExecution: React.FC = ({ // Look for the matching tool_use in previous assistant messages for (let i = index - 1; i >= 0; i--) { const prevMsg = messages[i]; - if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) { - const toolUse = prevMsg.message.content.find((c: any) => - c.type === 'tool_use' && c.id === content.tool_use_id + if ( + prevMsg.type === "assistant" && + prevMsg.message?.content && + Array.isArray(prevMsg.message.content) + ) { + const toolUse = prevMsg.message.content.find( + (c: any) => + c.type === "tool_use" && c.id === content.tool_use_id, ); if (toolUse) { const toolName = toolUse.name?.toLowerCase(); const toolsWithWidgets = [ - 'task', 'edit', 'multiedit', 'todowrite', 'ls', 'read', - 'glob', 'bash', 'write', 'grep' + "task", + "edit", + "multiedit", + "todowrite", + "ls", + "read", + "glob", + "bash", + "write", + "grep", ]; - if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) { + if ( + toolsWithWidgets.includes(toolName) || + toolUse.name?.startsWith("mcp__") + ) { willBeSkipped = true; } break; @@ -174,14 +201,14 @@ export const AgentExecution: React.FC = ({ } } } - + if (!willBeSkipped) { hasVisibleContent = true; break; } } } - + if (!hasVisibleContent) { return false; } @@ -210,7 +237,7 @@ export const AgentExecution: React.FC = ({ useEffect(() => { // Clean up listeners on unmount return () => { - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); if (elapsedTimeIntervalRef.current) { clearInterval(elapsedTimeIntervalRef.current); } @@ -219,7 +246,9 @@ export const AgentExecution: React.FC = ({ // Check if user is at the very bottom of the scrollable container const isAtBottom = () => { - const container = isFullscreenModalOpen ? fullscreenScrollRef.current : scrollContainerRef.current; + const container = isFullscreenModalOpen + ? fullscreenScrollRef.current + : scrollContainerRef.current; if (container) { const { scrollTop, scrollHeight, clientHeight } = container; const distanceFromBottom = scrollHeight - scrollTop - clientHeight; @@ -236,12 +265,24 @@ export const AgentExecution: React.FC = ({ if (shouldAutoScroll) { if (isFullscreenModalOpen) { - fullscreenRowVirtualizer.scrollToIndex(displayableMessages.length - 1, { align: "end", behavior: "smooth" }); + fullscreenRowVirtualizer.scrollToIndex(displayableMessages.length - 1, { + align: "end", + behavior: "smooth", + }); } else { - rowVirtualizer.scrollToIndex(displayableMessages.length - 1, { align: "end", behavior: "smooth" }); + rowVirtualizer.scrollToIndex(displayableMessages.length - 1, { + align: "end", + behavior: "smooth", + }); } } - }, [displayableMessages.length, hasUserScrolled, isFullscreenModalOpen, rowVirtualizer, fullscreenRowVirtualizer]); + }, [ + displayableMessages.length, + hasUserScrolled, + isFullscreenModalOpen, + rowVirtualizer, + fullscreenRowVirtualizer, + ]); // Update elapsed time while running useEffect(() => { @@ -254,7 +295,7 @@ export const AgentExecution: React.FC = ({ clearInterval(elapsedTimeIntervalRef.current); } } - + return () => { if (elapsedTimeIntervalRef.current) { clearInterval(elapsedTimeIntervalRef.current); @@ -266,7 +307,11 @@ export const AgentExecution: React.FC = ({ useEffect(() => { const tokens = messages.reduce((total, msg) => { if (msg.message?.usage) { - return total + msg.message.usage.input_tokens + msg.message.usage.output_tokens; + return ( + total + + msg.message.usage.input_tokens + + msg.message.usage.output_tokens + ); } if (msg.usage) { return total + msg.usage.input_tokens + msg.usage.output_tokens; @@ -276,7 +321,6 @@ export const AgentExecution: React.FC = ({ setTotalTokens(tokens); }, [messages]); - // Project path selection is handled upstream when opening an execution tab const handleOpenHooksDialog = async () => { @@ -287,99 +331,133 @@ export const AgentExecution: React.FC = ({ try { setIsRunning(true); // Update tab status to running - console.log('Setting tab status to running for tab:', tabId); + console.log("Setting tab status to running for tab:", tabId); if (tabId) { - updateTabStatus(tabId, 'running'); + updateTabStatus(tabId, "running"); } setExecutionStartTime(Date.now()); setMessages([]); setRawJsonlOutput([]); setRunId(null); - + // Clear any existing listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // Execute the agent and get the run ID - const executionRunId = await api.executeAgent(agent.id!, projectPath, task, model); + const executionRunId = await api.executeAgent( + agent.id!, + projectPath, + task, + model, + ); console.log("Agent execution started with run ID:", executionRunId); setRunId(executionRunId); - + // Track agent execution start trackEvent.agentStarted({ - agent_type: agent.name || 'custom', + agent_type: agent.name || "custom", agent_name: agent.name, - has_custom_prompt: task !== agent.default_task + has_custom_prompt: task !== agent.default_task, }); - + // Track feature adoption agentFeatureTracking.trackUsage(); - - // Set up event listeners with run ID isolation - const outputUnlisten = await listen(`agent-output:${executionRunId}`, (event) => { - try { - // Store raw JSONL - setRawJsonlOutput(prev => [...prev, event.payload]); - - // Parse and display - const message = JSON.parse(event.payload) as ClaudeStreamMessage; - setMessages(prev => [...prev, message]); - } catch (err) { - console.error("Failed to parse message:", err, event.payload); - } - }); - const errorUnlisten = await listen(`agent-error:${executionRunId}`, (event) => { - console.error("Agent error:", event.payload); - setError(event.payload); - - // Track agent error - trackEvent.agentError({ - error_type: 'runtime_error', - error_stage: 'execution', - retry_count: 0, - agent_type: agent.name || 'custom' - }); - }); + // Set up event listeners with run ID isolation + const outputUnlisten = await listen( + `agent-output:${executionRunId}`, + (event) => { + try { + // Store raw JSONL + setRawJsonlOutput((prev) => [...prev, event.payload]); - const completeUnlisten = await listen(`agent-complete:${executionRunId}`, (event) => { - setIsRunning(false); - const duration = executionStartTime ? Date.now() - executionStartTime : undefined; - setExecutionStartTime(null); - if (!event.payload) { - setError("Agent execution failed"); - // Update tab status to error - if (tabId) { - updateTabStatus(tabId, 'error'); + // Parse and display + const message = JSON.parse(event.payload) as ClaudeStreamMessage; + setMessages((prev) => [...prev, message]); + } catch (err) { + console.error("Failed to parse message:", err, event.payload); } - // Track both the old event for compatibility and the new error event - trackEvent.agentExecuted(agent.name || 'custom', false, agent.name, duration); + }, + ); + + const errorUnlisten = await listen( + `agent-error:${executionRunId}`, + (event) => { + console.error("Agent error:", event.payload); + setError(event.payload); + + // Track agent error trackEvent.agentError({ - error_type: 'execution_failed', - error_stage: 'completion', + error_type: "runtime_error", + error_stage: "execution", retry_count: 0, - agent_type: agent.name || 'custom' + agent_type: agent.name || "custom", }); - } else { - // Update tab status to complete on success - if (tabId) { - updateTabStatus(tabId, 'complete'); + }, + ); + + const completeUnlisten = await listen( + `agent-complete:${executionRunId}`, + (event) => { + setIsRunning(false); + const duration = executionStartTime + ? Date.now() - executionStartTime + : undefined; + setExecutionStartTime(null); + if (!event.payload) { + setError("Agent execution failed"); + // Update tab status to error + if (tabId) { + updateTabStatus(tabId, "error"); + } + // Track both the old event for compatibility and the new error event + trackEvent.agentExecuted( + agent.name || "custom", + false, + agent.name, + duration, + ); + trackEvent.agentError({ + error_type: "execution_failed", + error_stage: "completion", + retry_count: 0, + agent_type: agent.name || "custom", + }); + } else { + // Update tab status to complete on success + if (tabId) { + updateTabStatus(tabId, "complete"); + } + trackEvent.agentExecuted( + agent.name || "custom", + true, + agent.name, + duration, + ); } - trackEvent.agentExecuted(agent.name || 'custom', true, agent.name, duration); - } - }); + }, + ); - const cancelUnlisten = await listen(`agent-cancelled:${executionRunId}`, () => { - setIsRunning(false); - setExecutionStartTime(null); - setError("Agent execution was cancelled"); - // Update tab status to idle when cancelled - if (tabId) { - updateTabStatus(tabId, 'idle'); - } - }); + const cancelUnlisten = await listen( + `agent-cancelled:${executionRunId}`, + () => { + setIsRunning(false); + setExecutionStartTime(null); + setError("Agent execution was cancelled"); + // Update tab status to idle when cancelled + if (tabId) { + updateTabStatus(tabId, "idle"); + } + }, + ); - unlistenRefs.current = [outputUnlisten, errorUnlisten, completeUnlisten, cancelUnlisten]; + unlistenRefs.current = [ + outputUnlisten, + errorUnlisten, + completeUnlisten, + cancelUnlisten, + ]; } catch (err) { console.error("Failed to execute agent:", err); setIsRunning(false); @@ -387,20 +465,23 @@ export const AgentExecution: React.FC = ({ setRunId(null); // Update tab status to error if (tabId) { - updateTabStatus(tabId, 'error'); + updateTabStatus(tabId, "error"); } // Show error in messages - setMessages(prev => [...prev, { - type: "result", - subtype: "error", - is_error: true, - result: `Failed to execute agent: ${err instanceof Error ? err.message : 'Unknown error'}`, - duration_ms: 0, - usage: { - input_tokens: 0, - output_tokens: 0 - } - }]); + setMessages((prev) => [ + ...prev, + { + type: "result", + subtype: "error", + is_error: true, + result: `Failed to execute agent: ${err instanceof Error ? err.message : "Unknown error"}`, + duration_ms: 0, + usage: { + input_tokens: 0, + output_tokens: 0, + }, + }, + ]); } }; @@ -413,37 +494,42 @@ export const AgentExecution: React.FC = ({ // Call the API to kill the agent session const success = await api.killAgentSession(runId); - + if (success) { console.log(`Successfully stopped agent session ${runId}`); } else { - console.warn(`Failed to stop agent session ${runId} - it may have already finished`); + console.warn( + `Failed to stop agent session ${runId} - it may have already finished`, + ); } - + // Update UI state setIsRunning(false); setExecutionStartTime(null); // Update tab status to idle when stopped if (tabId) { - updateTabStatus(tabId, 'idle'); + updateTabStatus(tabId, "idle"); } - + // Clean up listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // Add a message indicating execution was stopped - setMessages(prev => [...prev, { - type: "result", - subtype: "error", - is_error: true, - result: "Execution stopped by user", - duration_ms: elapsedTime * 1000, - usage: { - input_tokens: totalTokens, - output_tokens: 0 - } - }]); + setMessages((prev) => [ + ...prev, + { + type: "result", + subtype: "error", + is_error: true, + result: "Execution stopped by user", + duration_ms: elapsedTime * 1000, + usage: { + input_tokens: totalTokens, + output_tokens: 0, + }, + }, + ]); } catch (err) { console.error("Failed to stop agent:", err); // Still update UI state even if the backend call failed @@ -451,21 +537,24 @@ export const AgentExecution: React.FC = ({ setExecutionStartTime(null); // Update tab status to idle if (tabId) { - updateTabStatus(tabId, 'idle'); + updateTabStatus(tabId, "idle"); } - + // Show error message - setMessages(prev => [...prev, { - type: "result", - subtype: "error", - is_error: true, - result: `Failed to stop execution: ${err instanceof Error ? err.message : 'Unknown error'}`, - duration_ms: elapsedTime * 1000, - usage: { - input_tokens: totalTokens, - output_tokens: 0 - } - }]); + setMessages((prev) => [ + ...prev, + { + type: "result", + subtype: "error", + is_error: true, + result: `Failed to stop execution: ${err instanceof Error ? err.message : "Unknown error"}`, + duration_ms: elapsedTime * 1000, + usage: { + input_tokens: totalTokens, + output_tokens: 0, + }, + }, + ]); } }; @@ -473,23 +562,23 @@ export const AgentExecution: React.FC = ({ if (isRunning) { // Show confirmation dialog before navigating away during execution const shouldLeave = window.confirm( - "An agent is currently running. If you navigate away, the agent will continue running in the background. You can view running sessions in the 'Running Sessions' tab within CC Agents.\n\nDo you want to continue?" + "An agent is currently running. If you navigate away, the agent will continue running in the background. You can view running sessions in the 'Running Sessions' tab within CC Agents.\n\nDo you want to continue?", ); if (!shouldLeave) { return; } } - + // Clean up listeners but don't stop the actual agent process - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // Navigate back onBack(); }; const handleCopyAsJsonl = async () => { - const jsonl = rawJsonlOutput.join('\n'); + const jsonl = rawJsonlOutput.join("\n"); await navigator.clipboard.writeText(jsonl); setCopyPopoverOpen(false); }; @@ -497,17 +586,17 @@ export const AgentExecution: React.FC = ({ const handleCopyAsMarkdown = async () => { let markdown = `# Agent Execution: ${agent.name}\n\n`; markdown += `**Task:** ${task}\n`; - markdown += `**Model:** ${model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'}\n`; + markdown += `**Model:** ${model === "opus" ? "Claude 4 Opus" : "Claude 4 Sonnet"}\n`; markdown += `**Date:** ${new Date().toISOString()}\n\n`; markdown += `---\n\n`; for (const msg of messages) { if (msg.type === "system" && msg.subtype === "init") { markdown += `## System Initialization\n\n`; - markdown += `- Session ID: \`${msg.session_id || 'N/A'}\`\n`; - markdown += `- Model: \`${msg.model || 'default'}\`\n`; + markdown += `- Session ID: \`${msg.session_id || "N/A"}\`\n`; + markdown += `- Model: \`${msg.model || "default"}\`\n`; if (msg.cwd) markdown += `- Working Directory: \`${msg.cwd}\`\n`; - if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(', ')}\n`; + if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(", ")}\n`; markdown += `\n`; } else if (msg.type === "assistant" && msg.message) { markdown += `## Assistant\n\n`; @@ -560,7 +649,6 @@ export const AgentExecution: React.FC = ({ setCopyPopoverOpen(false); }; - return (
{/* Fixed container that takes full height */} @@ -581,7 +669,12 @@ export const AgentExecution: React.FC = ({

{agent.name}

- {isRunning ? 'Running' : messages.length > 0 ? 'Complete' : 'Ready'} • {model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} + {isRunning + ? "Running" + : messages.length > 0 + ? "Complete" + : "Ready"}{" "} + • {model === "opus" ? "Claude 4 Opus" : "Claude 4 Sonnet"}

@@ -599,7 +692,7 @@ export const AgentExecution: React.FC = ({ - + {/* Configuration Section */}
@@ -619,7 +712,9 @@ export const AgentExecution: React.FC = ({ {/* Model Selection */}
- +
= ({ transition={{ duration: 0.15 }} className={cn( "flex-1 px-4 py-3 rounded-md border transition-all", - model === "sonnet" - ? "border-primary bg-primary/10 text-primary" + model === "sonnet" + ? "border-primary bg-primary/10 text-primary" : "border-border hover:border-primary/50 hover:bg-accent", - isRunning && "opacity-50 cursor-not-allowed" + isRunning && "opacity-50 cursor-not-allowed", )} disabled={isRunning} >
-
+
{model === "sonnet" && (
)}
-
Claude 4 Sonnet
-
Faster, efficient
+
+ Claude 4 Sonnet +
+
+ Faster, efficient +
- + !isRunning && setModel("opus")} @@ -658,25 +761,33 @@ export const AgentExecution: React.FC = ({ transition={{ duration: 0.15 }} className={cn( "flex-1 px-4 py-3 rounded-md border transition-all", - model === "opus" - ? "border-primary bg-primary/10 text-primary" + model === "opus" + ? "border-primary bg-primary/10 text-primary" : "border-border hover:border-primary/50 hover:bg-accent", - isRunning && "opacity-50 cursor-not-allowed" + isRunning && "opacity-50 cursor-not-allowed", )} disabled={isRunning} >
-
+
{model === "opus" && (
)}
-
Claude 4 Opus
-
More capable
+
+ Claude 4 Opus +
+
+ More capable +
@@ -686,7 +797,9 @@ export const AgentExecution: React.FC = ({ {/* Task Input */}
- + {projectPath && (
{projectPath && (

- Working in: {projectPath.split('/').pop() || projectPath} + Working in:{" "} + + {projectPath.split("/").pop() || projectPath} +

)}
@@ -749,7 +870,7 @@ export const AgentExecution: React.FC = ({ {/* Scrollable Output Display */}
-
{ @@ -757,7 +878,7 @@ export const AgentExecution: React.FC = ({ if (!hasUserScrolled) { setHasUserScrolled(true); } - + // If user scrolls back to bottom, re-enable auto-scroll if (isAtBottom()) { setHasUserScrolled(false); @@ -765,53 +886,60 @@ export const AgentExecution: React.FC = ({ }} >
- {messages.length === 0 && !isRunning && ( -
- -

Ready to Execute

-

- Enter a task to run the agent -

-
- )} + {messages.length === 0 && !isRunning && ( +
+ +

+ Ready to Execute +

+

+ Enter a task to run the agent +

+
+ )} - {isRunning && messages.length === 0 && ( -
-
- - Initializing agent... + {isRunning && messages.length === 0 && ( +
+
+ + + Initializing agent... + +
+ )} + +
+ + {rowVirtualizer.getVirtualItems().map((virtualItem) => { + const message = displayableMessages[virtualItem.index]; + return ( + el && rowVirtualizer.measureElement(el)} + initial={{ opacity: 0, y: 10 }} + animate={{ opacity: 1, y: 0 }} + transition={{ duration: 0.2 }} + className="absolute inset-x-4 pb-4" + style={{ top: virtualItem.start }} + > + + + + + ); + })} +
- )} -
- - {rowVirtualizer.getVirtualItems().map((virtualItem) => { - const message = displayableMessages[virtualItem.index]; - return ( - el && rowVirtualizer.measureElement(el)} - initial={{ opacity: 0, y: 10 }} - animate={{ opacity: 1, y: 0 }} - transition={{ duration: 0.2 }} - className="absolute inset-x-4 pb-4" - style={{ top: virtualItem.start }} - > - - - - - ); - })} - -
- -
+
@@ -836,7 +964,9 @@ export const AgentExecution: React.FC = ({ {isRunning && (
- Running + + Running +
)}
@@ -891,7 +1021,7 @@ export const AgentExecution: React.FC = ({ {/* Modal Content */}
-
{ @@ -899,7 +1029,7 @@ export const AgentExecution: React.FC = ({ if (!hasUserScrolled) { setHasUserScrolled(true); } - + // If user scrolls back to bottom, re-enable auto-scroll if (isAtBottom()) { setHasUserScrolled(false); @@ -920,38 +1050,49 @@ export const AgentExecution: React.FC = ({
- Initializing agent... + + Initializing agent... +
)}
- {fullscreenRowVirtualizer.getVirtualItems().map((virtualItem) => { - const message = displayableMessages[virtualItem.index]; - return ( - el && fullscreenRowVirtualizer.measureElement(el)} - initial={{ opacity: 0, y: 10 }} - animate={{ opacity: 1, y: 0 }} - transition={{ duration: 0.2 }} - className="absolute inset-x-4 pb-4" - style={{ top: virtualItem.start }} - > - - - - - ); - })} + {fullscreenRowVirtualizer + .getVirtualItems() + .map((virtualItem) => { + const message = displayableMessages[virtualItem.index]; + return ( + + el && fullscreenRowVirtualizer.measureElement(el) + } + initial={{ opacity: 0, y: 10 }} + animate={{ opacity: 1, y: 0 }} + transition={{ duration: 0.2 }} + className="absolute inset-x-4 pb-4" + style={{ top: virtualItem.start }} + > + + + + + ); + })}
- +
@@ -959,36 +1100,52 @@ export const AgentExecution: React.FC = ({ )} {/* Hooks Configuration Dialog */} - +
- Configure Hooks + + Configure Hooks + Configure hooks that run before, during, and after tool executions
- - + +
- + Project Settings - + Local Settings
- - + +

- Project hooks are stored in .claude/settings.json and - are committed to version control, allowing team members to share configurations. + Project hooks are stored in{" "} + + .claude/settings.json + {" "} + and are committed to version control, allowing team members + to share configurations.

= ({ />
- - + +

- Local hooks are stored in .claude/settings.local.json and - are not committed to version control, perfect for personal preferences. + Local hooks are stored in{" "} + + .claude/settings.local.json + {" "} + and are not committed to version control, perfect for + personal preferences.

{ type: "user", isMeta: true, message: { content: [] }, - timestamp: "2025-06-11T14:08:53.771Z" + timestamp: "2025-06-11T14:08:53.771Z", }, - + // Summary message { leafUuid: "3c5ecb4f-c1f0-40c2-a357-ab7642ad28b8", summary: "JSONL Viewer Model Configuration and Setup", - type: "summary" as any + type: "summary" as any, }, - + // Assistant with Edit tool { type: "assistant", message: { - content: [{ - type: "tool_use", - name: "Edit", - input: { - file_path: "/Users/mufeedvh/dev/jsonl-viewer/script.js", - new_string: "reader.onerror = () => reject(new Error('Failed to read file'));", - old_string: "reader.onerror = e => reject(new Error('Failed to read file'));" - } - }], - usage: { input_tokens: 4, output_tokens: 158 } - } + content: [ + { + type: "tool_use", + name: "Edit", + input: { + file_path: "/Users/mufeedvh/dev/jsonl-viewer/script.js", + new_string: + "reader.onerror = () => reject(new Error('Failed to read file'));", + old_string: + "reader.onerror = e => reject(new Error('Failed to read file'));", + }, + }, + ], + usage: { input_tokens: 4, output_tokens: 158 }, + }, }, - + // User with Edit tool result { type: "user", message: { - content: [{ - type: "tool_result", - content: `The file /Users/mufeedvh/dev/jsonl-viewer/script.js has been updated. Here's the result of running \`cat -n\` on a snippet of the edited file: + content: [ + { + type: "tool_result", + content: `The file /Users/mufeedvh/dev/jsonl-viewer/script.js has been updated. Here's the result of running \`cat -n\` on a snippet of the edited file: 220 readFileAsText(file) { 221 return new Promise((resolve, reject) => { 222 const reader = new FileReader(); @@ -55,45 +60,52 @@ export const AgentExecutionDemo: React.FC = () => { 225 reader.readAsText(file); 226 }); 227 } - 228` - }] - } + 228`, + }, + ], + }, }, - + // Assistant with MCP tool { type: "assistant", message: { - content: [{ - type: "tool_use", - name: "mcp__ide__getDiagnostics", - input: {} - }], - usage: { input_tokens: 4, output_tokens: 37 } - } + content: [ + { + type: "tool_use", + name: "mcp__ide__getDiagnostics", + input: {}, + }, + ], + usage: { input_tokens: 4, output_tokens: 37 }, + }, }, - + // User with empty tool result { type: "user", message: { - content: [{ - type: "tool_result", - content: "" - }] - } + content: [ + { + type: "tool_result", + content: "", + }, + ], + }, }, - + // Assistant with Write tool (large content) { type: "assistant", message: { - content: [{ - type: "tool_use", - name: "Write", - input: { - file_path: "/Users/mufeedvh/dev/jsonl-viewer/styles.css", - content: `/* Reset and Base Styles */ + content: [ + { + type: "tool_use", + name: "Write", + input: { + file_path: "/Users/mufeedvh/dev/jsonl-viewer/styles.css", + content: + `/* Reset and Base Styles */ * { margin: 0; padding: 0; @@ -162,20 +174,23 @@ body { /* ... many more lines of CSS ... */ /* This content is over 1000 characters so it should show the maximize button */ -` + '\n'.repeat(100) + '/* End of very long CSS file */' - } - }] - } - } +` + + "\n".repeat(100) + + "/* End of very long CSS file */", + }, + }, + ], + }, + }, ]; return (

Agent Execution Demo

- + {messages.map((message, idx) => ( ))}
); -}; \ No newline at end of file +}; diff --git a/src/components/AgentRunOutputViewer.tsx b/src/components/AgentRunOutputViewer.tsx index d83fb9317..2db24009e 100644 --- a/src/components/AgentRunOutputViewer.tsx +++ b/src/components/AgentRunOutputViewer.tsx @@ -1,32 +1,32 @@ -import { useState, useEffect, useRef, useMemo } from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { - Maximize2, - Minimize2, - Copy, - RefreshCw, - RotateCcw, +import { useState, useEffect, useRef, useMemo } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { + Maximize2, + Minimize2, + Copy, + RefreshCw, + RotateCcw, ChevronDown, Bot, Clock, Hash, DollarSign, - StopCircle -} from 'lucide-react'; -import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Toast, ToastContainer } from '@/components/ui/toast'; -import { Popover } from '@/components/ui/popover'; -import { api, type AgentRunWithMetrics } from '@/lib/api'; -import { useOutputCache } from '@/lib/outputCache'; -import { listen, type UnlistenFn } from '@tauri-apps/api/event'; -import { StreamMessage } from './StreamMessage'; -import { ErrorBoundary } from './ErrorBoundary'; -import { formatISOTimestamp } from '@/lib/date-utils'; -import { AGENT_ICONS } from './CCAgents'; -import type { ClaudeStreamMessage } from './AgentExecution'; -import { useTabState } from '@/hooks/useTabState'; + StopCircle, +} from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Toast, ToastContainer } from "@/components/ui/toast"; +import { Popover } from "@/components/ui/popover"; +import { api, type AgentRunWithMetrics } from "@/lib/api"; +import { useOutputCache } from "@/lib/outputCache"; +import { listen, type UnlistenFn } from "@tauri-apps/api/event"; +import { StreamMessage } from "./StreamMessage"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { formatISOTimestamp } from "@/lib/date-utils"; +import { AGENT_ICONS } from "./CCAgents"; +import type { ClaudeStreamMessage } from "./AgentExecution"; +import { useTabState } from "@/hooks/useTabState"; interface AgentRunOutputViewerProps { /** @@ -45,17 +45,17 @@ interface AgentRunOutputViewerProps { /** * AgentRunOutputViewer - Modal component for viewing agent execution output - * + * * @example * setSelectedRun(null)} * /> */ -export function AgentRunOutputViewer({ - agentRunId, +export function AgentRunOutputViewer({ + agentRunId, tabId, - className + className, }: AgentRunOutputViewerProps) { const { updateTabTitle, updateTabStatus } = useTabState(); const [run, setRun] = useState(null); @@ -64,14 +64,17 @@ export function AgentRunOutputViewer({ const [loading, setLoading] = useState(true); const [isFullscreen, setIsFullscreen] = useState(false); const [refreshing, setRefreshing] = useState(false); - const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null); + const [toast, setToast] = useState<{ + message: string; + type: "success" | "error"; + } | null>(null); const [copyPopoverOpen, setCopyPopoverOpen] = useState(false); const [hasUserScrolled, setHasUserScrolled] = useState(false); - + // Track whether we're in the initial load phase const isInitialLoadRef = useRef(true); const hasSetupListenersRef = useRef(false); - + const scrollAreaRef = useRef(null); const outputEndRef = useRef(null); const fullscreenScrollRef = useRef(null); @@ -81,7 +84,9 @@ export function AgentRunOutputViewer({ // Auto-scroll logic const isAtBottom = () => { - const container = isFullscreen ? fullscreenScrollRef.current : scrollAreaRef.current; + const container = isFullscreen + ? fullscreenScrollRef.current + : scrollAreaRef.current; if (container) { const { scrollTop, scrollHeight, clientHeight } = container; const distanceFromBottom = scrollHeight - scrollTop - clientHeight; @@ -92,9 +97,11 @@ export function AgentRunOutputViewer({ const scrollToBottom = () => { if (!hasUserScrolled) { - const endRef = isFullscreen ? fullscreenMessagesEndRef.current : outputEndRef.current; + const endRef = isFullscreen + ? fullscreenMessagesEndRef.current + : outputEndRef.current; if (endRef) { - endRef.scrollIntoView({ behavior: 'smooth' }); + endRef.scrollIntoView({ behavior: "smooth" }); } } }; @@ -106,16 +113,23 @@ export function AgentRunOutputViewer({ setLoading(true); const agentRun = await api.getAgentRun(parseInt(agentRunId)); setRun(agentRun); - updateTabTitle(tabId, `Agent: ${agentRun.agent_name || 'Unknown'}`); - updateTabStatus(tabId, agentRun.status === 'running' ? 'running' : agentRun.status === 'failed' ? 'error' : 'complete'); + updateTabTitle(tabId, `Agent: ${agentRun.agent_name || "Unknown"}`); + updateTabStatus( + tabId, + agentRun.status === "running" + ? "running" + : agentRun.status === "failed" + ? "error" + : "complete", + ); } catch (error) { - console.error('Failed to load agent run:', error); - updateTabStatus(tabId, 'error'); + console.error("Failed to load agent run:", error); + updateTabStatus(tabId, "error"); } finally { setLoading(false); } }; - + if (agentRunId) { loadAgentRun(); } @@ -124,7 +138,7 @@ export function AgentRunOutputViewer({ // Cleanup on unmount useEffect(() => { return () => { - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; hasSetupListenersRef.current = false; }; @@ -141,11 +155,11 @@ export function AgentRunOutputViewer({ const loadOutput = async (skipCache = false) => { if (!run?.id) return; - console.log('[AgentRunOutputViewer] Loading output for run:', { + console.log("[AgentRunOutputViewer] Loading output for run:", { runId: run.id, status: run.status, sessionId: run.session_id, - skipCache + skipCache, }); try { @@ -153,13 +167,20 @@ export function AgentRunOutputViewer({ if (!skipCache) { const cached = getCachedOutput(run.id); if (cached) { - console.log('[AgentRunOutputViewer] Found cached output'); - const cachedJsonlLines = cached.output.split('\n').filter(line => line.trim()); + console.log("[AgentRunOutputViewer] Found cached output"); + const cachedJsonlLines = cached.output + .split("\n") + .filter((line) => line.trim()); setRawJsonlOutput(cachedJsonlLines); setMessages(cached.messages); // If cache is recent (less than 5 seconds old) and session isn't running, use cache only - if (Date.now() - cached.lastUpdated < 5000 && run.status !== 'running') { - console.log('[AgentRunOutputViewer] Using recent cache, skipping refresh'); + if ( + Date.now() - cached.lastUpdated < 5000 && + run.status !== "running" + ) { + console.log( + "[AgentRunOutputViewer] Using recent cache, skipping refresh", + ); return; } } @@ -168,93 +189,131 @@ export function AgentRunOutputViewer({ setLoading(true); // If we have a session_id, try to load from JSONL file first - if (run.session_id && run.session_id !== '') { - console.log('[AgentRunOutputViewer] Attempting to load from JSONL with session_id:', run.session_id); + if (run.session_id && run.session_id !== "") { + console.log( + "[AgentRunOutputViewer] Attempting to load from JSONL with session_id:", + run.session_id, + ); try { const history = await api.loadAgentSessionHistory(run.session_id); - console.log('[AgentRunOutputViewer] Successfully loaded JSONL history:', history.length, 'messages'); - + console.log( + "[AgentRunOutputViewer] Successfully loaded JSONL history:", + history.length, + "messages", + ); + // Convert history to messages format - const loadedMessages: ClaudeStreamMessage[] = history.map(entry => ({ - ...entry, - type: entry.type || "assistant" - })); - + const loadedMessages: ClaudeStreamMessage[] = history.map( + (entry) => ({ + ...entry, + type: entry.type || "assistant", + }), + ); + setMessages(loadedMessages); - setRawJsonlOutput(history.map(h => JSON.stringify(h))); - + setRawJsonlOutput(history.map((h) => JSON.stringify(h))); + // Update cache setCachedOutput(run.id, { - output: history.map(h => JSON.stringify(h)).join('\n'), + output: history.map((h) => JSON.stringify(h)).join("\n"), messages: loadedMessages, lastUpdated: Date.now(), - status: run.status + status: run.status, }); - + // Set up live event listeners for running sessions - if (run.status === 'running') { - console.log('[AgentRunOutputViewer] Setting up live listeners for running session'); + if (run.status === "running") { + console.log( + "[AgentRunOutputViewer] Setting up live listeners for running session", + ); setupLiveEventListeners(); - + try { await api.streamSessionOutput(run.id); } catch (streamError) { - console.warn('[AgentRunOutputViewer] Failed to start streaming, will poll instead:', streamError); + console.warn( + "[AgentRunOutputViewer] Failed to start streaming, will poll instead:", + streamError, + ); } } - + return; } catch (err) { - console.warn('[AgentRunOutputViewer] Failed to load from JSONL:', err); - console.warn('[AgentRunOutputViewer] Falling back to regular output method'); + console.warn( + "[AgentRunOutputViewer] Failed to load from JSONL:", + err, + ); + console.warn( + "[AgentRunOutputViewer] Falling back to regular output method", + ); } } else { - console.log('[AgentRunOutputViewer] No session_id available, using fallback method'); + console.log( + "[AgentRunOutputViewer] No session_id available, using fallback method", + ); } // Fallback to the original method if JSONL loading fails or no session_id - console.log('[AgentRunOutputViewer] Using getSessionOutput fallback'); + console.log("[AgentRunOutputViewer] Using getSessionOutput fallback"); const rawOutput = await api.getSessionOutput(run.id); - console.log('[AgentRunOutputViewer] Received raw output:', rawOutput.length, 'characters'); - + console.log( + "[AgentRunOutputViewer] Received raw output:", + rawOutput.length, + "characters", + ); + // Parse JSONL output into messages - const jsonlLines = rawOutput.split('\n').filter(line => line.trim()); + const jsonlLines = rawOutput.split("\n").filter((line) => line.trim()); setRawJsonlOutput(jsonlLines); - + const parsedMessages: ClaudeStreamMessage[] = []; for (const line of jsonlLines) { try { const message = JSON.parse(line) as ClaudeStreamMessage; parsedMessages.push(message); } catch (err) { - console.error("[AgentRunOutputViewer] Failed to parse message:", err, line); + console.error( + "[AgentRunOutputViewer] Failed to parse message:", + err, + line, + ); } } - console.log('[AgentRunOutputViewer] Parsed', parsedMessages.length, 'messages from output'); + console.log( + "[AgentRunOutputViewer] Parsed", + parsedMessages.length, + "messages from output", + ); setMessages(parsedMessages); - + // Update cache setCachedOutput(run.id, { output: rawOutput, messages: parsedMessages, lastUpdated: Date.now(), - status: run.status + status: run.status, }); - + // Set up live event listeners for running sessions - if (run.status === 'running') { - console.log('[AgentRunOutputViewer] Setting up live listeners for running session (fallback)'); + if (run.status === "running") { + console.log( + "[AgentRunOutputViewer] Setting up live listeners for running session (fallback)", + ); setupLiveEventListeners(); - + try { await api.streamSessionOutput(run.id); } catch (streamError) { - console.warn('[AgentRunOutputViewer] Failed to start streaming (fallback), will poll instead:', streamError); + console.warn( + "[AgentRunOutputViewer] Failed to start streaming (fallback), will poll instead:", + streamError, + ); } } } catch (error) { - console.error('Failed to load agent output:', error); - setToast({ message: 'Failed to load agent output', type: 'error' }); + console.error("Failed to load agent output:", error); + setToast({ message: "Failed to load agent output", type: "error" }); } finally { setLoading(false); } @@ -263,15 +322,15 @@ export function AgentRunOutputViewer({ // Set up live event listeners for running sessions const setupLiveEventListeners = async () => { if (!run?.id || hasSetupListenersRef.current) return; - + try { // Clean up existing listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; // Mark that we've set up listeners hasSetupListenersRef.current = true; - + // After setup, we're no longer in initial load // Small delay to ensure any pending messages are processed setTimeout(() => { @@ -279,71 +338,100 @@ export function AgentRunOutputViewer({ }, 100); // Set up live event listeners with run ID isolation - const outputUnlisten = await listen(`agent-output:${run!.id}`, (event) => { - try { - // Skip messages during initial load phase - if (isInitialLoadRef.current) { - console.log('[AgentRunOutputViewer] Skipping message during initial load'); - return; - } - - // Store raw JSONL - setRawJsonlOutput(prev => [...prev, event.payload]); - - // Parse and display - const message = JSON.parse(event.payload) as ClaudeStreamMessage; - setMessages(prev => [...prev, message]); - } catch (err) { - console.error("[AgentRunOutputViewer] Failed to parse message:", err, event.payload); - } - }); - - const errorUnlisten = await listen(`agent-error:${run!.id}`, (event) => { - console.error("[AgentRunOutputViewer] Agent error:", event.payload); - setToast({ message: event.payload, type: 'error' }); - }); - - const completeUnlisten = await listen(`agent-complete:${run!.id}`, () => { - setToast({ message: 'Agent execution completed', type: 'success' }); - // Don't set status here as the parent component should handle it - }); - - const cancelUnlisten = await listen(`agent-cancelled:${run!.id}`, () => { - setToast({ message: 'Agent execution was cancelled', type: 'error' }); - }); + const outputUnlisten = await listen( + `agent-output:${run!.id}`, + (event) => { + try { + // Skip messages during initial load phase + if (isInitialLoadRef.current) { + console.log( + "[AgentRunOutputViewer] Skipping message during initial load", + ); + return; + } - unlistenRefs.current = [outputUnlisten, errorUnlisten, completeUnlisten, cancelUnlisten]; + // Store raw JSONL + setRawJsonlOutput((prev) => [...prev, event.payload]); + + // Parse and display + const message = JSON.parse(event.payload) as ClaudeStreamMessage; + setMessages((prev) => [...prev, message]); + } catch (err) { + console.error( + "[AgentRunOutputViewer] Failed to parse message:", + err, + event.payload, + ); + } + }, + ); + + const errorUnlisten = await listen( + `agent-error:${run!.id}`, + (event) => { + console.error("[AgentRunOutputViewer] Agent error:", event.payload); + setToast({ message: event.payload, type: "error" }); + }, + ); + + const completeUnlisten = await listen( + `agent-complete:${run!.id}`, + () => { + setToast({ message: "Agent execution completed", type: "success" }); + // Don't set status here as the parent component should handle it + }, + ); + + const cancelUnlisten = await listen( + `agent-cancelled:${run!.id}`, + () => { + setToast({ message: "Agent execution was cancelled", type: "error" }); + }, + ); + + unlistenRefs.current = [ + outputUnlisten, + errorUnlisten, + completeUnlisten, + cancelUnlisten, + ]; } catch (error) { - console.error('[AgentRunOutputViewer] Failed to set up live event listeners:', error); + console.error( + "[AgentRunOutputViewer] Failed to set up live event listeners:", + error, + ); } }; // Copy functionality const handleCopyAsJsonl = async () => { - const jsonl = rawJsonlOutput.join('\n'); + const jsonl = rawJsonlOutput.join("\n"); await navigator.clipboard.writeText(jsonl); setCopyPopoverOpen(false); - setToast({ message: 'Output copied as JSONL', type: 'success' }); + setToast({ message: "Output copied as JSONL", type: "success" }); }; const handleCopyAsMarkdown = async () => { if (!run) return; let markdown = `# Agent Execution: ${run.agent_name}\n\n`; markdown += `**Task:** ${run.task}\n`; - markdown += `**Model:** ${run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'}\n`; + markdown += `**Model:** ${run.model === "opus" ? "Claude 4 Opus" : "Claude 4 Sonnet"}\n`; markdown += `**Date:** ${formatISOTimestamp(run.created_at)}\n`; - if (run.metrics?.duration_ms) markdown += `**Duration:** ${(run.metrics.duration_ms / 1000).toFixed(2)}s\n`; - if (run.metrics?.total_tokens) markdown += `**Total Tokens:** ${run.metrics.total_tokens}\n`; - if (run.metrics?.cost_usd) markdown += `**Cost:** $${run.metrics.cost_usd.toFixed(4)} USD\n`; + if (run.metrics?.duration_ms) + markdown += `**Duration:** ${(run.metrics.duration_ms / 1000).toFixed(2)}s\n`; + if (run.metrics?.total_tokens) + markdown += `**Total Tokens:** ${run.metrics.total_tokens}\n`; + if (run.metrics?.cost_usd) + markdown += `**Cost:** $${run.metrics.cost_usd.toFixed(4)} USD\n`; markdown += `\n---\n\n`; for (const msg of messages) { if (msg.type === "system" && msg.subtype === "init") { markdown += `## System Initialization\n\n`; - markdown += `- Session ID: \`${msg.session_id || 'N/A'}\`\n`; - markdown += `- Model: \`${msg.model || 'default'}\`\n`; + markdown += `- Session ID: \`${msg.session_id || "N/A"}\`\n`; + markdown += `- Model: \`${msg.model || "default"}\`\n`; if (msg.cwd) markdown += `- Working Directory: \`${msg.cwd}\`\n`; - if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(', ')}\n`; + if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(", ")}\n`; markdown += `\n`; } else if (msg.type === "assistant" && msg.message) { markdown += `## Assistant\n\n`; @@ -381,7 +469,7 @@ export function AgentRunOutputViewer({ await navigator.clipboard.writeText(markdown); setCopyPopoverOpen(false); - setToast({ message: 'Output copied as Markdown', type: 'success' }); + setToast({ message: "Output copied as Markdown", type: "success" }); }; const handleRefresh = async () => { @@ -392,23 +480,25 @@ export function AgentRunOutputViewer({ const handleStop = async () => { if (!run?.id) { - console.error('[AgentRunOutputViewer] No run ID available to stop'); + console.error("[AgentRunOutputViewer] No run ID available to stop"); return; } try { // Call the API to kill the agent session const success = await api.killAgentSession(run.id); - + if (success) { - console.log(`[AgentRunOutputViewer] Successfully stopped agent session ${run.id}`); - setToast({ message: 'Agent execution stopped', type: 'success' }); - + console.log( + `[AgentRunOutputViewer] Successfully stopped agent session ${run.id}`, + ); + setToast({ message: "Agent execution stopped", type: "success" }); + // Clean up listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; hasSetupListenersRef.current = false; - + // Add a message indicating execution was stopped const stopMessage: ClaudeStreamMessage = { type: "result", @@ -418,25 +508,30 @@ export function AgentRunOutputViewer({ duration_ms: 0, usage: { input_tokens: 0, - output_tokens: 0 - } + output_tokens: 0, + }, }; - setMessages(prev => [...prev, stopMessage]); - + setMessages((prev) => [...prev, stopMessage]); + // Update the tab status - updateTabStatus(tabId, 'idle'); - + updateTabStatus(tabId, "idle"); + // Refresh the output to get updated status await loadOutput(true); } else { - console.warn(`[AgentRunOutputViewer] Failed to stop agent session ${run.id} - it may have already finished`); - setToast({ message: 'Failed to stop agent - it may have already finished', type: 'error' }); + console.warn( + `[AgentRunOutputViewer] Failed to stop agent session ${run.id} - it may have already finished`, + ); + setToast({ + message: "Failed to stop agent - it may have already finished", + type: "error", + }); } } catch (err) { - console.error('[AgentRunOutputViewer] Failed to stop agent:', err); - setToast({ - message: `Failed to stop execution: ${err instanceof Error ? err.message : 'Unknown error'}`, - type: 'error' + console.error("[AgentRunOutputViewer] Failed to stop agent:", err); + setToast({ + message: `Failed to stop execution: ${err instanceof Error ? err.message : "Unknown error"}`, + type: "error", }); } }; @@ -451,15 +546,17 @@ export function AgentRunOutputViewer({ // Load output on mount useEffect(() => { if (!run?.id) return; - + // Check cache immediately for instant display const cached = getCachedOutput(run!.id); if (cached) { - const cachedJsonlLines = cached.output.split('\n').filter(line => line.trim()); + const cachedJsonlLines = cached.output + .split("\n") + .filter((line) => line.trim()); setRawJsonlOutput(cachedJsonlLines); setMessages(cached.messages); } - + // Then load fresh data loadOutput(); }, [run?.id]); @@ -472,12 +569,19 @@ export function AgentRunOutputViewer({ if (message.isMeta) return false; const msg = message.message; - if (!msg.content || (Array.isArray(msg.content) && msg.content.length === 0)) return false; + if ( + !msg.content || + (Array.isArray(msg.content) && msg.content.length === 0) + ) + return false; if (Array.isArray(msg.content)) { let hasVisibleContent = false; for (const content of msg.content) { - if (content.type === "text") { hasVisibleContent = true; break; } + if (content.type === "text") { + hasVisibleContent = true; + break; + } if (content.type === "tool_result") { // Check if this tool result will be displayed as a widget let willBeSkipped = false; @@ -485,12 +589,33 @@ export function AgentRunOutputViewer({ // Find the corresponding tool use for (let i = messages.indexOf(message) - 1; i >= 0; i--) { const prevMsg = messages[i]; - if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) { - const toolUse = prevMsg.message.content.find((c: any) => c.type === 'tool_use' && c.id === content.tool_use_id); + if ( + prevMsg.type === "assistant" && + prevMsg.message?.content && + Array.isArray(prevMsg.message.content) + ) { + const toolUse = prevMsg.message.content.find( + (c: any) => + c.type === "tool_use" && c.id === content.tool_use_id, + ); if (toolUse) { const toolName = toolUse.name?.toLowerCase(); - const toolsWithWidgets = ['task','edit','multiedit','todowrite','ls','read','glob','bash','write','grep']; - if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) { + const toolsWithWidgets = [ + "task", + "edit", + "multiedit", + "todowrite", + "ls", + "read", + "glob", + "bash", + "write", + "grep", + ]; + if ( + toolsWithWidgets.includes(toolName) || + toolUse.name?.startsWith("mcp__") + ) { willBeSkipped = true; } break; @@ -498,7 +623,10 @@ export function AgentRunOutputViewer({ } } } - if (!willBeSkipped) { hasVisibleContent = true; break; } + if (!willBeSkipped) { + hasVisibleContent = true; + break; + } } } if (!hasVisibleContent) return false; @@ -543,21 +671,21 @@ export function AgentRunOutputViewer({ return ( <> -
+
- -
+ +
-
- {renderIcon(run.agent_icon)} -
+
{renderIcon(run.agent_icon)}
{run.agent_name} - {run.status === 'running' && ( + {run.status === "running" && (
- Running + + Running +
)}
@@ -566,7 +694,9 @@ export function AgentRunOutputViewer({

- {run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} + {run.model === "opus" + ? "Claude 4 Opus" + : "Claude 4 Sonnet"}
@@ -593,11 +723,7 @@ export function AgentRunOutputViewer({
+ - {run.status === 'running' && ( + {run.status === "running" && (
-
- - - {loading ? ( +
+ + + {loading ? (
@@ -678,29 +808,34 @@ export function AgentRunOutputViewer({

No output available yet

) : ( -
- {displayableMessages.map((message: ClaudeStreamMessage, index: number) => ( - - - - - - ))} + {displayableMessages.map( + (message: ClaudeStreamMessage, index: number) => ( + + + + + + ), + )}
- )} - + )} +
@@ -718,10 +853,7 @@ export function AgentRunOutputViewer({
+ - {run.status === 'running' && ( + {run.status === "running" && (
-
- {displayableMessages.map((message: ClaudeStreamMessage, index: number) => ( - - - - - - ))} + {displayableMessages.map( + (message: ClaudeStreamMessage, index: number) => ( + + + + + + ), + )}
@@ -826,4 +965,4 @@ export function AgentRunOutputViewer({ ); } -export default AgentRunOutputViewer; \ No newline at end of file +export default AgentRunOutputViewer; diff --git a/src/components/AgentRunView.tsx b/src/components/AgentRunView.tsx index 16ade6c51..f83d7e892 100644 --- a/src/components/AgentRunView.tsx +++ b/src/components/AgentRunView.tsx @@ -1,14 +1,14 @@ import React, { useState, useEffect } from "react"; import { motion } from "framer-motion"; -import { - ArrowLeft, - Copy, - ChevronDown, +import { + ArrowLeft, + Copy, + ChevronDown, Clock, Hash, DollarSign, Bot, - StopCircle + StopCircle, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; @@ -39,7 +39,7 @@ interface AgentRunViewProps { /** * AgentRunView component for viewing past agent execution details - * + * * @example * setView('list')} /> */ @@ -64,30 +64,35 @@ export const AgentRunView: React.FC = ({ setError(null); const runData = await api.getAgentRunWithRealTimeMetrics(runId); setRun(runData); - + // If we have a session_id, try to load from JSONL file first - if (runData.session_id && runData.session_id !== '') { + if (runData.session_id && runData.session_id !== "") { try { const history = await api.loadAgentSessionHistory(runData.session_id); - + // Convert history to messages format - const loadedMessages: ClaudeStreamMessage[] = history.map(entry => ({ - ...entry, - type: entry.type || "assistant" - })); - + const loadedMessages: ClaudeStreamMessage[] = history.map( + (entry) => ({ + ...entry, + type: entry.type || "assistant", + }), + ); + setMessages(loadedMessages); return; } catch (err) { - console.warn('Failed to load from JSONL, falling back to output field:', err); + console.warn( + "Failed to load from JSONL, falling back to output field:", + err, + ); } } - + // Fallback: Parse JSONL output from the output field if (runData.output) { const parsedMessages: ClaudeStreamMessage[] = []; - const lines = runData.output.split('\n').filter(line => line.trim()); - + const lines = runData.output.split("\n").filter((line) => line.trim()); + for (const line of lines) { try { const msg = JSON.parse(line) as ClaudeStreamMessage; @@ -96,7 +101,7 @@ export const AgentRunView: React.FC = ({ console.error("Failed to parse line:", line, err); } } - + setMessages(parsedMessages); } } catch (err) { @@ -115,14 +120,14 @@ export const AgentRunView: React.FC = ({ const handleCopyAsMarkdown = async () => { if (!run) return; - + let markdown = `# Agent Run: ${run.agent_name}\n\n`; markdown += `**Task:** ${run.task}\n`; markdown += `**Model:** ${run.model}\n`; markdown += `**Status:** ${run.status}\n`; if (run.metrics) { - markdown += `**Tokens:** ${run.metrics.total_tokens || 'N/A'}\n`; - markdown += `**Cost:** $${run.metrics.cost_usd?.toFixed(4) || 'N/A'}\n`; + markdown += `**Tokens:** ${run.metrics.total_tokens || "N/A"}\n`; + markdown += `**Cost:** $${run.metrics.cost_usd?.toFixed(4) || "N/A"}\n`; } markdown += `**Date:** ${new Date(run.created_at).toISOString()}\n\n`; markdown += `---\n\n`; @@ -130,10 +135,10 @@ export const AgentRunView: React.FC = ({ for (const msg of messages) { if (msg.type === "system" && msg.subtype === "init") { markdown += `## System Initialization\n\n`; - markdown += `- Session ID: \`${msg.session_id || 'N/A'}\`\n`; - markdown += `- Model: \`${msg.model || 'default'}\`\n`; + markdown += `- Session ID: \`${msg.session_id || "N/A"}\`\n`; + markdown += `- Model: \`${msg.model || "default"}\`\n`; if (msg.cwd) markdown += `- Working Directory: \`${msg.cwd}\`\n`; - if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(', ')}\n`; + if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(", ")}\n`; markdown += `\n`; } else if (msg.type === "assistant" && msg.message) { markdown += `## Assistant\n\n`; @@ -175,22 +180,24 @@ export const AgentRunView: React.FC = ({ const handleStop = async () => { if (!runId) { - console.error('[AgentRunView] No run ID available to stop'); + console.error("[AgentRunView] No run ID available to stop"); return; } try { // Call the API to kill the agent session const success = await api.killAgentSession(runId); - + if (success) { - console.log(`[AgentRunView] Successfully stopped agent session ${runId}`); - + console.log( + `[AgentRunView] Successfully stopped agent session ${runId}`, + ); + // Update the run status locally if (run) { - setRun({ ...run, status: 'cancelled' }); + setRun({ ...run, status: "cancelled" }); } - + // Add a message indicating execution was stopped const stopMessage: ClaudeStreamMessage = { type: "result", @@ -200,20 +207,22 @@ export const AgentRunView: React.FC = ({ duration_ms: 0, usage: { input_tokens: 0, - output_tokens: 0 - } + output_tokens: 0, + }, }; - setMessages(prev => [...prev, stopMessage]); - + setMessages((prev) => [...prev, stopMessage]); + // Reload the run data after a short delay setTimeout(() => { loadRun(); }, 1000); } else { - console.warn(`[AgentRunView] Failed to stop agent session ${runId} - it may have already finished`); + console.warn( + `[AgentRunView] Failed to stop agent session ${runId} - it may have already finished`, + ); } } catch (err) { - console.error('[AgentRunView] Failed to stop agent:', err); + console.error("[AgentRunView] Failed to stop agent:", err); } }; @@ -232,7 +241,12 @@ export const AgentRunView: React.FC = ({ if (error || !run) { return ( -
+

{error || "Run not found"}

@@ -262,13 +276,15 @@ export const AgentRunView: React.FC = ({ {renderIcon(run.agent_icon)}

{run.agent_name}

-

Execution History

+

+ Execution History +

- +
- {run?.status === 'running' && ( + {run?.status === "running" && ( )} - + = ({ />
- + {/* Run Details */}

Task:

-

{run.task}

+

+ {run.task} +

- {run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} + {run.model === "opus" ? "Claude 4 Opus" : "Claude 4 Sonnet"}
- +
{formatISOTimestamp(run.created_at)}
- + {run.metrics?.duration_ms && (
{(run.metrics.duration_ms / 1000).toFixed(2)}s
)} - + {run.metrics?.total_tokens && (
{run.metrics.total_tokens} tokens
)} - + {run.metrics?.cost_usd && (
@@ -382,4 +400,4 @@ export const AgentRunView: React.FC = ({
); -}; \ No newline at end of file +}; diff --git a/src/components/AgentRunsList.tsx b/src/components/AgentRunsList.tsx index deb403c37..3ccd9e2f1 100644 --- a/src/components/AgentRunsList.tsx +++ b/src/components/AgentRunsList.tsx @@ -29,7 +29,7 @@ const ITEMS_PER_PAGE = 5; /** * AgentRunsList component - Displays a paginated list of agent execution runs - * + * * @example * = ({ }) => { const [currentPage, setCurrentPage] = useState(1); const { createAgentTab } = useTabState(); - + // Calculate pagination const totalPages = Math.ceil(runs.length / ITEMS_PER_PAGE); const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = startIndex + ITEMS_PER_PAGE; const currentRuns = runs.slice(startIndex, endIndex); - + // Reset to page 1 if runs change React.useEffect(() => { setCurrentPage(1); }, [runs.length]); - + const renderIcon = (iconName: string) => { const Icon = AGENT_ICONS[iconName as keyof typeof AGENT_ICONS] || Bot; return ; }; - + const formatDuration = (ms?: number) => { if (!ms) return "N/A"; const seconds = Math.floor(ms / 1000); @@ -68,7 +68,7 @@ export const AgentRunsList: React.FC = ({ const remainingSeconds = seconds % 60; return `${minutes}m ${remainingSeconds}s`; }; - + const formatTokens = (tokens?: number) => { if (!tokens) return "0"; if (tokens >= 1000) { @@ -76,7 +76,7 @@ export const AgentRunsList: React.FC = ({ } return tokens.toString(); }; - + const handleRunClick = (run: AgentRunWithMetrics) => { // If there's a callback, use it (for full-page navigation) if (onRunClick) { @@ -86,7 +86,7 @@ export const AgentRunsList: React.FC = ({ createAgentTab(run.id.toString(), run.agent_name); } }; - + if (runs.length === 0) { return (
@@ -115,7 +115,7 @@ export const AgentRunsList: React.FC = ({ handleRunClick(run)} > @@ -124,7 +124,7 @@ export const AgentRunsList: React.FC = ({
{renderIcon(run.agent_icon)}
- +

@@ -133,48 +133,58 @@ export const AgentRunsList: React.FC = ({ {run.status === "running" && (
- Running + + Running +
)}

- +

{run.task}

- +
{formatISOTimestamp(run.created_at)}
- + {run.metrics?.duration_ms && ( {formatDuration(run.metrics.duration_ms)} )} - + {run.metrics?.total_tokens && (
- {formatTokens(run.metrics.total_tokens)} + + {formatTokens(run.metrics.total_tokens)} +
)}
- +
- - {run.status === "completed" ? "Completed" : - run.status === "running" ? "Running" : - run.status === "failed" ? "Failed" : - "Pending"} + {run.status === "completed" + ? "Completed" + : run.status === "running" + ? "Running" + : run.status === "failed" + ? "Failed" + : "Pending"}
@@ -183,7 +193,7 @@ export const AgentRunsList: React.FC = ({ ))} - + {/* Pagination */} {totalPages > 1 && (
@@ -195,7 +205,6 @@ export const AgentRunsList: React.FC = ({
)}
- ); -}; \ No newline at end of file +}; diff --git a/src/components/AgentsModal.tsx b/src/components/AgentsModal.tsx index f5eb4063c..096db1add 100644 --- a/src/components/AgentsModal.tsx +++ b/src/components/AgentsModal.tsx @@ -1,44 +1,64 @@ -import React, { useState, useEffect } from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { Bot, Plus, Loader2, Play, Clock, CheckCircle, XCircle, Trash2, Import, ChevronDown, FileJson, Globe, Download } from 'lucide-react'; +import React, { useState, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { + Bot, + Plus, + Loader2, + Play, + Clock, + CheckCircle, + XCircle, + Trash2, + Import, + ChevronDown, + FileJson, + Globe, + Download, +} from "lucide-react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, -} from '@/components/ui/dialog'; +} from "@/components/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { Toast } from '@/components/ui/toast'; -import { api, type Agent, type AgentRunWithMetrics } from '@/lib/api'; -import { useTabState } from '@/hooks/useTabState'; -import { formatISOTimestamp } from '@/lib/date-utils'; -import { open as openDialog, save } from '@tauri-apps/plugin-dialog'; -import { invoke } from '@tauri-apps/api/core'; -import { GitHubAgentBrowser } from '@/components/GitHubAgentBrowser'; +} from "@/components/ui/dropdown-menu"; +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Toast } from "@/components/ui/toast"; +import { api, type Agent, type AgentRunWithMetrics } from "@/lib/api"; +import { useTabState } from "@/hooks/useTabState"; +import { formatISOTimestamp } from "@/lib/date-utils"; +import { open as openDialog, save } from "@tauri-apps/plugin-dialog"; +import { invoke } from "@tauri-apps/api/core"; +import { GitHubAgentBrowser } from "@/components/GitHubAgentBrowser"; interface AgentsModalProps { open: boolean; onOpenChange: (open: boolean) => void; } -export const AgentsModal: React.FC = ({ open, onOpenChange }) => { - const [activeTab, setActiveTab] = useState('agents'); +export const AgentsModal: React.FC = ({ + open, + onOpenChange, +}) => { + const [activeTab, setActiveTab] = useState("agents"); const [agents, setAgents] = useState([]); const [runningAgents, setRunningAgents] = useState([]); const [loading, setLoading] = useState(true); const [agentToDelete, setAgentToDelete] = useState(null); const [showDeleteDialog, setShowDeleteDialog] = useState(false); - const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null); + const [toast, setToast] = useState<{ + message: string; + type: "success" | "error"; + } | null>(null); const [showGitHubBrowser, setShowGitHubBrowser] = useState(false); const { createAgentTab, createCreateAgentTab } = useTabState(); @@ -53,7 +73,7 @@ export const AgentsModal: React.FC = ({ open, onOpenChange }) // Refresh running agents periodically useEffect(() => { if (!open) return; - + const interval = setInterval(() => { loadRunningAgents(); }, 3000); // Refresh every 3 seconds @@ -67,7 +87,7 @@ export const AgentsModal: React.FC = ({ open, onOpenChange }) const agentList = await api.listAgents(); setAgents(agentList); } catch (error) { - console.error('Failed to load agents:', error); + console.error("Failed to load agents:", error); } finally { setLoading(false); } @@ -76,52 +96,60 @@ export const AgentsModal: React.FC = ({ open, onOpenChange }) const loadRunningAgents = async () => { try { const runs = await api.listRunningAgentSessions(); - const agentRuns = runs.map(run => ({ - id: run.id, - agent_id: run.agent_id, - agent_name: run.agent_name, - task: run.task, - model: run.model, - status: 'running' as const, - created_at: run.created_at, - project_path: run.project_path, - } as AgentRunWithMetrics)); - + const agentRuns = runs.map( + (run) => + ({ + id: run.id, + agent_id: run.agent_id, + agent_name: run.agent_name, + task: run.task, + model: run.model, + status: "running" as const, + created_at: run.created_at, + project_path: run.project_path, + }) as AgentRunWithMetrics, + ); + setRunningAgents(agentRuns); } catch (error) { - console.error('Failed to load running agents:', error); + console.error("Failed to load running agents:", error); } }; const handleRunAgent = async (agent: Agent) => { // Open directory picker for project path - const { open } = await import('@tauri-apps/plugin-dialog'); - + const { open } = await import("@tauri-apps/plugin-dialog"); + try { const projectPath = await open({ directory: true, multiple: false, - title: `Select project directory for ${agent.name}` + title: `Select project directory for ${agent.name}`, }); - + if (!projectPath) { // User cancelled return; } - + // Create a new agent execution tab const tabId = `agent-exec-${agent.id}-${Date.now()}`; - + // Close modal onOpenChange(false); - + // Dispatch event to open agent execution in the new tab with project path - window.dispatchEvent(new CustomEvent('open-agent-execution', { - detail: { agent, tabId, projectPath } - })); + window.dispatchEvent( + new CustomEvent("open-agent-execution", { + detail: { agent, tabId, projectPath }, + }), + ); } catch (error) { - console.error('Failed to run agent:', error); - setToast({ message: `Failed to run agent: ${agent.name}`, type: 'error' }); + console.error("Failed to run agent:", error); + setToast({ + message: `Failed to run agent: ${agent.name}`, + type: "error", + }); } }; @@ -138,7 +166,7 @@ export const AgentsModal: React.FC = ({ open, onOpenChange }) setShowDeleteDialog(false); setAgentToDelete(null); } catch (error) { - console.error('Failed to delete agent:', error); + console.error("Failed to delete agent:", error); } }; @@ -158,19 +186,24 @@ export const AgentsModal: React.FC = ({ open, onOpenChange }) try { const filePath = await openDialog({ multiple: false, - filters: [{ - name: 'JSON', - extensions: ['json'] - }] + filters: [ + { + name: "JSON", + extensions: ["json"], + }, + ], }); - + if (filePath) { const agent = await api.importAgentFromFile(filePath as string); loadAgents(); // Refresh list - setToast({ message: `Agent "${agent.name}" imported successfully`, type: "success" }); + setToast({ + message: `Agent "${agent.name}" imported successfully`, + type: "success", + }); } } catch (error) { - console.error('Failed to import agent:', error); + console.error("Failed to import agent:", error); setToast({ message: "Failed to import agent", type: "error" }); } }; @@ -183,30 +216,35 @@ export const AgentsModal: React.FC = ({ open, onOpenChange }) try { const exportData = await api.exportAgent(agent.id!); const filePath = await save({ - defaultPath: `${agent.name.toLowerCase().replace(/\s+/g, '-')}.json`, - filters: [{ - name: 'JSON', - extensions: ['json'] - }] + defaultPath: `${agent.name.toLowerCase().replace(/\s+/g, "-")}.json`, + filters: [ + { + name: "JSON", + extensions: ["json"], + }, + ], }); - + if (filePath) { - await invoke('write_file', { path: filePath, content: JSON.stringify(exportData, null, 2) }); + await invoke("write_file", { + path: filePath, + content: JSON.stringify(exportData, null, 2), + }); setToast({ message: "Agent exported successfully", type: "success" }); } } catch (error) { - console.error('Failed to export agent:', error); + console.error("Failed to export agent:", error); setToast({ message: "Failed to export agent", type: "error" }); } }; const getStatusIcon = (status: string) => { switch (status) { - case 'running': + case "running": return ; - case 'completed': + case "completed": return ; - case 'failed': + case "failed": return ; default: return ; @@ -216,245 +254,260 @@ export const AgentsModal: React.FC = ({ open, onOpenChange }) return ( <> - - - - - Agent Management - - - Create new agents or manage running agent executions - - - - - - Available Agents - - Running Agents - {runningAgents.length > 0 && ( - - {runningAgents.length} - - )} - - - -
- - - {/* Action buttons at the top */} -
- - - - - - - - - From File - - - - From GitHub - - - -
- {loading ? ( -
- -
- ) : agents.length === 0 ? ( -
- -

No agents available

-

- Create your first agent to get started -

- + + + + + + + + From File + + + + From GitHub + + +
- ) : ( -
- {agents.map((agent) => ( - + +
+ ) : agents.length === 0 ? ( +
+ +

+ No agents available +

+

+ Create your first agent to get started +

+ - - -
-
- - ))} -
- )} - - - - - - {runningAgents.length === 0 ? ( -
- -

No running agents

-

- Agent executions will appear here when started -

-
- ) : ( -
- - {runningAgents.map((run) => ( + + Create Agent + +
+ ) : ( +
+ {agents.map((agent) => ( handleOpenAgentRun(run)} + key={agent.id} + initial={{ opacity: 0, y: 10 }} + animate={{ opacity: 1, y: 0 }} + className="p-4 border rounded-lg hover:bg-muted/50 transition-colors" >

- {getStatusIcon(run.status)} - {run.agent_name} + + {agent.name}

-

- {run.task} -

-
- Started: {formatISOTimestamp(run.created_at)} - - {run.model === 'opus' ? 'Claude 4 Opus' : 'Claude 4 Sonnet'} - -
+ {agent.default_task && ( +

+ {agent.default_task} +

+ )} +
+
+ + +
-
))} - -
- )} -
-
+
+ )} + + + + + + {runningAgents.length === 0 ? ( +
+ +

+ No running agents +

+

+ Agent executions will appear here when started +

+
+ ) : ( +
+ + {runningAgents.map((run) => ( + handleOpenAgentRun(run)} + > +
+
+

+ {getStatusIcon(run.status)} + {run.agent_name} +

+

+ {run.task} +

+
+ + Started:{" "} + {formatISOTimestamp(run.created_at)} + + + {run.model === "opus" + ? "Claude 4 Opus" + : "Claude 4 Sonnet"} + +
+
+ +
+
+ ))} +
+
+ )} +
+
+
+ + +
+ + {/* Delete Confirmation Dialog */} + + + + Delete Agent + + Are you sure you want to delete "{agentToDelete?.name}"? This + action cannot be undone. + + +
+ +
- -
-
- - {/* Delete Confirmation Dialog */} - - - - Delete Agent - - Are you sure you want to delete "{agentToDelete?.name}"? This action cannot be undone. - - -
- - -
-
-
- - {/* GitHub Agent Browser */} - setShowGitHubBrowser(false)} - onImportSuccess={() => { - setShowGitHubBrowser(false); - loadAgents(); // Refresh the agents list - setToast({ message: "Agent imported successfully", type: "success" }); - }} - /> - - {/* Toast notifications */} - {toast && ( - setToast(null)} + +
+ + {/* GitHub Agent Browser */} + setShowGitHubBrowser(false)} + onImportSuccess={() => { + setShowGitHubBrowser(false); + loadAgents(); // Refresh the agents list + setToast({ message: "Agent imported successfully", type: "success" }); + }} /> - )} + + {/* Toast notifications */} + {toast && ( + setToast(null)} + /> + )} ); }; -export default AgentsModal; \ No newline at end of file +export default AgentsModal; diff --git a/src/components/AnalyticsErrorBoundary.tsx b/src/components/AnalyticsErrorBoundary.tsx index 41ffd4e2b..bcb4ae965 100644 --- a/src/components/AnalyticsErrorBoundary.tsx +++ b/src/components/AnalyticsErrorBoundary.tsx @@ -1,5 +1,5 @@ -import React, { Component, ErrorInfo, ReactNode } from 'react'; -import { eventBuilders, analytics } from '@/lib/analytics'; +import React, { Component, ErrorInfo, ReactNode } from "react"; +import { eventBuilders, analytics } from "@/lib/analytics"; interface Props { children: ReactNode; @@ -27,15 +27,15 @@ export class AnalyticsErrorBoundary extends Component { componentDidCatch(error: Error, errorInfo: ErrorInfo) { // Track UI error to analytics const event = eventBuilders.uiError({ - component_name: errorInfo.componentStack?.split('\n')[0] || 'Unknown', - error_type: error.name || 'UnknownError', + component_name: errorInfo.componentStack?.split("\n")[0] || "Unknown", + error_type: error.name || "UnknownError", user_action: undefined, // Could be enhanced with context }); - + analytics.track(event.event, event.properties); - + // Log to console for debugging - console.error('UI Error caught by boundary:', error, errorInfo); + console.error("UI Error caught by boundary:", error, errorInfo); } reset = () => { @@ -48,7 +48,7 @@ export class AnalyticsErrorBoundary extends Component { if (this.props.fallback) { return this.props.fallback(this.state.error, this.reset); } - + // Default fallback UI return (
@@ -77,11 +77,11 @@ export class AnalyticsErrorBoundary extends Component { */ export function withAnalyticsErrorBoundary

( Component: React.ComponentType

, - fallback?: (error: Error, reset: () => void) => ReactNode + fallback?: (error: Error, reset: () => void) => ReactNode, ) { return (props: P) => ( ); -} \ No newline at end of file +} diff --git a/src/components/App.cleaned.tsx b/src/components/App.cleaned.tsx index a6c7000bd..d6feb5655 100644 --- a/src/components/App.cleaned.tsx +++ b/src/components/App.cleaned.tsx @@ -15,35 +15,38 @@ import { useTabState } from "@/hooks/useTabState"; * AppContent component - Contains the main app logic, wrapped by providers */ function AppContent() { - const { } = useTabState(); + const {} = useTabState(); const [showNFO, setShowNFO] = useState(false); const [showClaudeBinaryDialog, setShowClaudeBinaryDialog] = useState(false); - const [toast, setToast] = useState<{ message: string; type: "success" | "error" | "info" } | null>(null); + const [toast, setToast] = useState<{ + message: string; + type: "success" | "error" | "info"; + } | null>(null); const [showAgentsModal, setShowAgentsModal] = useState(false); const [, setClaudeExecutableExists] = useState(true); // Keyboard shortcuts for tab navigation useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { - const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0; const modKey = isMac ? e.metaKey : e.ctrlKey; - + if (modKey) { switch (e.key) { - case 't': + case "t": e.preventDefault(); - window.dispatchEvent(new CustomEvent('create-chat-tab')); + window.dispatchEvent(new CustomEvent("create-chat-tab")); break; - case 'w': + case "w": e.preventDefault(); - window.dispatchEvent(new CustomEvent('close-current-tab')); + window.dispatchEvent(new CustomEvent("close-current-tab")); break; - case 'Tab': + case "Tab": e.preventDefault(); if (e.shiftKey) { - window.dispatchEvent(new CustomEvent('switch-to-previous-tab')); + window.dispatchEvent(new CustomEvent("switch-to-previous-tab")); } else { - window.dispatchEvent(new CustomEvent('switch-to-next-tab')); + window.dispatchEvent(new CustomEvent("switch-to-next-tab")); } break; default: @@ -51,15 +54,17 @@ function AppContent() { const num = parseInt(e.key); if (!isNaN(num) && num >= 1 && num <= 9) { e.preventDefault(); - window.dispatchEvent(new CustomEvent('switch-to-tab', { detail: num - 1 })); + window.dispatchEvent( + new CustomEvent("switch-to-tab", { detail: num - 1 }), + ); } break; } } }; - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); }, []); // Check if Claude executable exists on mount @@ -82,27 +87,27 @@ function AppContent() { // Custom event handlers useEffect(() => { const handleCreateProjectTab = () => { - window.dispatchEvent(new CustomEvent('create-project-tab')); + window.dispatchEvent(new CustomEvent("create-project-tab")); }; const handleShowNFO = () => setShowNFO(true); const handleShowAgents = () => setShowAgentsModal(true); - const projectButton = document.getElementById('create-project-tab-btn'); + const projectButton = document.getElementById("create-project-tab-btn"); if (projectButton) { - projectButton.addEventListener('click', handleCreateProjectTab); + projectButton.addEventListener("click", handleCreateProjectTab); } // Listen for custom events to show modals - window.addEventListener('show-nfo', handleShowNFO); - window.addEventListener('show-agents-modal', handleShowAgents); + window.addEventListener("show-nfo", handleShowNFO); + window.addEventListener("show-agents-modal", handleShowAgents); return () => { if (projectButton) { - projectButton.removeEventListener('click', handleCreateProjectTab); + projectButton.removeEventListener("click", handleCreateProjectTab); } - window.removeEventListener('show-nfo', handleShowNFO); - window.removeEventListener('show-agents-modal', handleShowAgents); + window.removeEventListener("show-nfo", handleShowNFO); + window.removeEventListener("show-agents-modal", handleShowAgents); }; }, []); @@ -118,11 +123,11 @@ function AppContent() { { // Open settings tab or modal - window.dispatchEvent(new CustomEvent('create-settings-tab')); + window.dispatchEvent(new CustomEvent("create-settings-tab")); }} onAgentsClick={() => {}} /> - + {/* Tab-based interface */}

@@ -131,23 +136,23 @@ function AppContent() { {/* Global Modals */} {showNFO && setShowNFO(false)} />} - - { setClaudeExecutableExists(true); - setToast({ message: "Claude binary path set successfully", type: "success" }); + setToast({ + message: "Claude binary path set successfully", + type: "success", + }); }} onError={(message) => { setToast({ message, type: "error" }); }} /> - - + + {/* Toast Container */} {toast && ( @@ -177,4 +182,4 @@ function App() { ); } -export default App; \ No newline at end of file +export default App; diff --git a/src/components/CheckpointSettings.tsx b/src/components/CheckpointSettings.tsx index 4f2549836..3ddab0041 100644 --- a/src/components/CheckpointSettings.tsx +++ b/src/components/CheckpointSettings.tsx @@ -1,12 +1,12 @@ import React, { useState, useEffect } from "react"; import { motion } from "framer-motion"; -import { +import { Wrench, Save, Trash2, HardDrive, AlertCircle, - Loader2 + Loader2, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; @@ -27,9 +27,9 @@ interface CheckpointSettingsProps { /** * CheckpointSettings component for managing checkpoint configuration - * + * * @example - * = ({ className, }) => { const [autoCheckpointEnabled, setAutoCheckpointEnabled] = useState(true); - const [checkpointStrategy, setCheckpointStrategy] = useState("smart"); + const [checkpointStrategy, setCheckpointStrategy] = + useState("smart"); const [totalCheckpoints, setTotalCheckpoints] = useState(0); const [keepCount, setKeepCount] = useState(10); const [isLoading, setIsLoading] = useState(false); @@ -65,8 +66,12 @@ export const CheckpointSettings: React.FC = ({ try { setIsLoading(true); setError(null); - - const settings = await api.getCheckpointSettings(sessionId, projectId, projectPath); + + const settings = await api.getCheckpointSettings( + sessionId, + projectId, + projectPath, + ); setAutoCheckpointEnabled(settings.auto_checkpoint_enabled); setCheckpointStrategy(settings.checkpoint_strategy); setTotalCheckpoints(settings.total_checkpoints); @@ -83,15 +88,15 @@ export const CheckpointSettings: React.FC = ({ setIsSaving(true); setError(null); setSuccessMessage(null); - + await api.updateCheckpointSettings( sessionId, projectId, projectPath, autoCheckpointEnabled, - checkpointStrategy + checkpointStrategy, ); - + setSuccessMessage("Settings saved successfully"); setTimeout(() => setSuccessMessage(null), 3000); } catch (err) { @@ -107,17 +112,17 @@ export const CheckpointSettings: React.FC = ({ setIsLoading(true); setError(null); setSuccessMessage(null); - + const removed = await api.cleanupOldCheckpoints( sessionId, projectId, projectPath, - keepCount + keepCount, ); - + setSuccessMessage(`Removed ${removed} old checkpoints`); setTimeout(() => setSuccessMessage(null), 3000); - + // Reload settings to get updated count await loadSettings(); } catch (err) { @@ -143,8 +148,12 @@ export const CheckpointSettings: React.FC = ({
-

Checkpoint Settings

-

Manage session checkpoints and recovery

+

+ Checkpoint Settings +

+

+ Manage session checkpoints and recovery +

@@ -154,9 +163,12 @@ export const CheckpointSettings: React.FC = ({
-

Experimental Feature

+

+ Experimental Feature +

- Checkpointing may affect directory structure or cause data loss. Use with caution. + Checkpointing may affect directory structure or cause data loss. + Use with caution.

@@ -183,7 +195,9 @@ export const CheckpointSettings: React.FC = ({ transition={{ duration: 0.15 }} className="rounded-md border border-green-600/50 bg-green-50 dark:bg-green-950/20 p-3" > - {successMessage} + + {successMessage} + )} @@ -192,7 +206,9 @@ export const CheckpointSettings: React.FC = ({ {/* Auto-checkpoint toggle */}
- +

Automatically create checkpoints based on the selected strategy

@@ -207,26 +223,31 @@ export const CheckpointSettings: React.FC = ({ {/* Checkpoint strategy */}
- + setCheckpointStrategy(value as CheckpointStrategy)} + onValueChange={(value: string) => + setCheckpointStrategy(value as CheckpointStrategy) + } options={strategyOptions} disabled={isLoading || !autoCheckpointEnabled} />

- {checkpointStrategy === "manual" && "Checkpoints will only be created manually"} - {checkpointStrategy === "per_prompt" && "A checkpoint will be created after each user prompt"} - {checkpointStrategy === "per_tool_use" && "A checkpoint will be created after each tool use"} - {checkpointStrategy === "smart" && "Checkpoints will be created after destructive operations"} + {checkpointStrategy === "manual" && + "Checkpoints will only be created manually"} + {checkpointStrategy === "per_prompt" && + "A checkpoint will be created after each user prompt"} + {checkpointStrategy === "per_tool_use" && + "A checkpoint will be created after each tool use"} + {checkpointStrategy === "smart" && + "Checkpoints will be created after destructive operations"}

{/* Save button */} - +

- Total checkpoints: {totalCheckpoints} + Total checkpoints:{" "} + + {totalCheckpoints} +

{/* Cleanup settings */}
- +
= ({ ); -}; \ No newline at end of file +}; diff --git a/src/components/ClaudeBinaryDialog.tsx b/src/components/ClaudeBinaryDialog.tsx index eb72ccd62..ff424eea7 100644 --- a/src/components/ClaudeBinaryDialog.tsx +++ b/src/components/ClaudeBinaryDialog.tsx @@ -1,8 +1,21 @@ import { useState, useEffect } from "react"; import { api, type ClaudeInstallation } from "@/lib/api"; import { Button } from "@/components/ui/button"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { ExternalLink, FileQuestion, Terminal, AlertCircle, Loader2 } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { + ExternalLink, + FileQuestion, + Terminal, + AlertCircle, + Loader2, +} from "lucide-react"; import { ClaudeVersionSelector } from "./ClaudeVersionSelector"; interface ClaudeBinaryDialogProps { @@ -12,8 +25,14 @@ interface ClaudeBinaryDialogProps { onError: (message: string) => void; } -export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: ClaudeBinaryDialogProps) { - const [selectedInstallation, setSelectedInstallation] = useState(null); +export function ClaudeBinaryDialog({ + open, + onOpenChange, + onSuccess, + onError, +}: ClaudeBinaryDialogProps) { + const [selectedInstallation, setSelectedInstallation] = + useState(null); const [isValidating, setIsValidating] = useState(false); const [hasInstallations, setHasInstallations] = useState(true); const [checkingInstallations, setCheckingInstallations] = useState(true); @@ -50,7 +69,11 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C onOpenChange(false); } catch (error) { console.error("Failed to save Claude binary path:", error); - onError(error instanceof Error ? error.message : "Failed to save Claude binary path"); + onError( + error instanceof Error + ? error.message + : "Failed to save Claude binary path", + ); } finally { setIsValidating(false); } @@ -68,24 +91,27 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C {checkingInstallations ? (
- Searching for Claude installations... + + Searching for Claude installations... +
) : hasInstallations ? (

- Multiple Claude Code installations were found on your system. + Multiple Claude Code installations were found on your system. Please select which one you'd like to use.

) : ( <>

- Claude Code was not found in any of the common installation locations. - Please install Claude Code to continue. + Claude Code was not found in any of the common installation + locations. Please install Claude Code to continue.

- Searched locations: PATH, /usr/local/bin, - /opt/homebrew/bin, ~/.nvm/versions/node/*/bin, ~/.claude/local, ~/.local/bin + Searched locations:{" "} + PATH, /usr/local/bin, /opt/homebrew/bin, + ~/.nvm/versions/node/*/bin, ~/.claude/local, ~/.local/bin

@@ -94,8 +120,11 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C

- Tip: You can install Claude Code using{" "} - npm install -g @claude + Tip: You can install + Claude Code using{" "} + + npm install -g @claude +

)} @@ -114,7 +143,12 @@ export function ClaudeBinaryDialog({ open, onOpenChange, onSuccess, onError }: C - ); -} \ No newline at end of file +} diff --git a/src/components/ClaudeCodeSession.refactored.tsx b/src/components/ClaudeCodeSession.refactored.tsx index ae70af687..d13c42131 100644 --- a/src/components/ClaudeCodeSession.refactored.tsx +++ b/src/components/ClaudeCodeSession.refactored.tsx @@ -6,12 +6,22 @@ import { Label } from "@/components/ui/label"; import { api, type Session } from "@/lib/api"; import { cn } from "@/lib/utils"; import { open } from "@tauri-apps/plugin-dialog"; -import { FloatingPromptInput, type FloatingPromptInputRef } from "./FloatingPromptInput"; +import { + FloatingPromptInput, + type FloatingPromptInputRef, +} from "./FloatingPromptInput"; import { ErrorBoundary } from "./ErrorBoundary"; import { TimelineNavigator } from "./TimelineNavigator"; import { CheckpointSettings } from "./CheckpointSettings"; import { SlashCommandsManager } from "./SlashCommandsManager"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; import { SplitPane } from "@/components/ui/split-pane"; import { WebviewPreview } from "./WebviewPreview"; @@ -39,7 +49,9 @@ export const ClaudeCodeSession: React.FC = ({ className, onStreamingChange, }) => { - const [projectPath, setProjectPath] = useState(initialProjectPath || session?.project_path || ""); + const [projectPath, setProjectPath] = useState( + initialProjectPath || session?.project_path || "", + ); const [error, setError] = useState(null); const [copyPopoverOpen, setCopyPopoverOpen] = useState(false); const [isFirstPrompt, setIsFirstPrompt] = useState(!session); @@ -48,10 +60,13 @@ export const ClaudeCodeSession: React.FC = ({ const [showTimeline, setShowTimeline] = useState(false); const [showSettings, setShowSettings] = useState(false); const [showForkDialog, setShowForkDialog] = useState(false); - const [showSlashCommandsSettings, setShowSlashCommandsSettings] = useState(false); + const [showSlashCommandsSettings, setShowSlashCommandsSettings] = + useState(false); const [forkCheckpointId, setForkCheckpointId] = useState(null); const [forkSessionName, setForkSessionName] = useState(""); - const [queuedPrompts, setQueuedPrompts] = useState>([]); + const [queuedPrompts, setQueuedPrompts] = useState< + Array<{ id: string; prompt: string; model: "sonnet" | "opus" }> + >([]); const [showPreview, setShowPreview] = useState(false); const [previewUrl, setPreviewUrl] = useState(null); const [isPreviewMaximized, setIsPreviewMaximized] = useState(false); @@ -65,13 +80,13 @@ export const ClaudeCodeSession: React.FC = ({ isStreaming, currentSessionId: _currentSessionId, clearMessages, - loadMessages + loadMessages, } = useClaudeMessages({ onSessionInfo: (info) => { setClaudeSessionId(info.sessionId); }, onTokenUpdate: setTotalTokens, - onStreamingChange + onStreamingChange, }); const { @@ -80,14 +95,14 @@ export const ClaudeCodeSession: React.FC = ({ loadCheckpoints, createCheckpoint: _createCheckpoint, restoreCheckpoint, - forkCheckpoint + forkCheckpoint, } = useCheckpoints({ sessionId: claudeSessionId, - projectId: session?.project_id || '', + projectId: session?.project_id || "", projectPath: projectPath, - onToast: (message: string, type: 'success' | 'error') => { + onToast: (message: string, type: "success" | "error") => { console.log(`Toast: ${type} - ${message}`); - } + }, }); // Handle path selection @@ -95,10 +110,10 @@ export const ClaudeCodeSession: React.FC = ({ const selected = await open({ directory: true, multiple: false, - title: "Select Project Directory" + title: "Select Project Directory", }); - - if (selected && typeof selected === 'string') { + + if (selected && typeof selected === "string") { setProjectPath(selected); setError(null); setIsFirstPrompt(true); @@ -106,38 +121,43 @@ export const ClaudeCodeSession: React.FC = ({ }; // Handle sending prompts - const handleSendPrompt = useCallback(async (prompt: string, model: "sonnet" | "opus") => { - if (!projectPath || !prompt.trim()) return; - - // Add to queue if streaming - if (isStreaming) { - const id = Date.now().toString(); - setQueuedPrompts(prev => [...prev, { id, prompt, model }]); - return; - } + const handleSendPrompt = useCallback( + async (prompt: string, model: "sonnet" | "opus") => { + if (!projectPath || !prompt.trim()) return; - try { - setError(null); - - if (isFirstPrompt) { - await api.executeClaudeCode(projectPath, prompt, model); - setIsFirstPrompt(false); - } else if (claudeSessionId) { - await api.continueClaudeCode(projectPath, prompt, model); + // Add to queue if streaming + if (isStreaming) { + const id = Date.now().toString(); + setQueuedPrompts((prev) => [...prev, { id, prompt, model }]); + return; } - } catch (error) { - console.error("Failed to send prompt:", error); - setError(error instanceof Error ? error.message : "Failed to send prompt"); - } - }, [projectPath, isStreaming, isFirstPrompt, claudeSessionId]); + + try { + setError(null); + + if (isFirstPrompt) { + await api.executeClaudeCode(projectPath, prompt, model); + setIsFirstPrompt(false); + } else if (claudeSessionId) { + await api.continueClaudeCode(projectPath, prompt, model); + } + } catch (error) { + console.error("Failed to send prompt:", error); + setError( + error instanceof Error ? error.message : "Failed to send prompt", + ); + } + }, + [projectPath, isStreaming, isFirstPrompt, claudeSessionId], + ); // Process queued prompts const processQueuedPrompts = useCallback(async () => { if (queuedPrompts.length === 0 || isStreaming) return; const nextPrompt = queuedPrompts[0]; - setQueuedPrompts(prev => prev.slice(1)); - + setQueuedPrompts((prev) => prev.slice(1)); + await handleSendPrompt(nextPrompt.prompt, nextPrompt.model); }, [queuedPrompts, isStreaming, handleSendPrompt]); @@ -146,7 +166,7 @@ export const ClaudeCodeSession: React.FC = ({ if (!isStreaming && queuedPrompts.length > 0) { processQueueTimeoutRef.current = setTimeout(processQueuedPrompts, 500); } - + return () => { if (processQueueTimeoutRef.current) { clearTimeout(processQueueTimeoutRef.current); @@ -157,7 +177,7 @@ export const ClaudeCodeSession: React.FC = ({ // Copy handlers const handleCopyAsJsonl = async () => { try { - await navigator.clipboard.writeText(rawJsonlOutput.join('\n')); + await navigator.clipboard.writeText(rawJsonlOutput.join("\n")); setCopyPopoverOpen(false); console.log("Session output copied as JSONL"); } catch (error) { @@ -168,25 +188,28 @@ export const ClaudeCodeSession: React.FC = ({ const handleCopyAsMarkdown = async () => { try { const markdown = messages - .filter(msg => msg.type === 'user' || msg.type === 'assistant') - .map(msg => { - if (msg.type === 'user') { - return `## User\n\n${msg.message || ''}`; - } else if (msg.type === 'assistant' && msg.message?.content) { - const content = Array.isArray(msg.message.content) - ? msg.message.content.map((item: any) => { - if (typeof item === 'string') return item; - if (item.type === 'text') return item.text; - return ''; - }).filter(Boolean).join('') + .filter((msg) => msg.type === "user" || msg.type === "assistant") + .map((msg) => { + if (msg.type === "user") { + return `## User\n\n${msg.message || ""}`; + } else if (msg.type === "assistant" && msg.message?.content) { + const content = Array.isArray(msg.message.content) + ? msg.message.content + .map((item: any) => { + if (typeof item === "string") return item; + if (item.type === "text") return item.text; + return ""; + }) + .filter(Boolean) + .join("") : msg.message.content; return `## Assistant\n\n${content}`; } - return ''; + return ""; }) .filter(Boolean) - .join('\n\n---\n\n'); - + .join("\n\n---\n\n"); + await navigator.clipboard.writeText(markdown); setCopyPopoverOpen(false); console.log("Session output copied as Markdown"); @@ -205,7 +228,10 @@ export const ClaudeCodeSession: React.FC = ({ const handleConfirmFork = async () => { if (!forkCheckpointId || !forkSessionName.trim()) return; - const forkedSession = await forkCheckpoint(forkCheckpointId, forkSessionName); + const forkedSession = await forkCheckpoint( + forkCheckpointId, + forkSessionName, + ); if (forkedSession) { setShowForkDialog(false); // Navigate to forked session @@ -248,7 +274,9 @@ export const ClaudeCodeSession: React.FC = ({ onCopyAsJsonl={handleCopyAsJsonl} onCopyAsMarkdown={handleCopyAsMarkdown} onToggleTimeline={() => setShowTimeline(!showTimeline)} - onProjectSettings={onProjectSettings ? () => onProjectSettings(projectPath) : undefined} + onProjectSettings={ + onProjectSettings ? () => onProjectSettings(projectPath) : undefined + } onSlashCommandsSettings={() => setShowSlashCommandsSettings(true)} setCopyPopoverOpen={setCopyPopoverOpen} /> @@ -268,7 +296,11 @@ export const ClaudeCodeSession: React.FC = ({ /> setQueuedPrompts(prev => prev.filter(p => p.id !== id))} + onRemove={(id) => + setQueuedPrompts((prev) => + prev.filter((p) => p.id !== id), + ) + } />
} @@ -278,7 +310,9 @@ export const ClaudeCodeSession: React.FC = ({ isMaximized={isPreviewMaximized} onClose={() => setShowPreview(false)} onUrlChange={setPreviewUrl} - onToggleMaximize={() => setIsPreviewMaximized(!isPreviewMaximized)} + onToggleMaximize={() => + setIsPreviewMaximized(!isPreviewMaximized) + } /> } initialSplit={60} @@ -294,7 +328,9 @@ export const ClaudeCodeSession: React.FC = ({ /> setQueuedPrompts(prev => prev.filter(p => p.id !== id))} + onRemove={(id) => + setQueuedPrompts((prev) => prev.filter((p) => p.id !== id)) + } />
)} @@ -356,9 +392,7 @@ export const ClaudeCodeSession: React.FC = ({ )} {showSlashCommandsSettings && projectPath && ( - + )} {/* Fork dialog */} @@ -367,7 +401,8 @@ export const ClaudeCodeSession: React.FC = ({ Fork Session from Checkpoint - Create a new session branching from this checkpoint. The original session will remain unchanged. + Create a new session branching from this checkpoint. The + original session will remain unchanged.
@@ -383,10 +418,13 @@ export const ClaudeCodeSession: React.FC = ({
- -
); -}; \ No newline at end of file +}; diff --git a/src/components/ClaudeCodeSession.tsx b/src/components/ClaudeCodeSession.tsx index d4563e242..384444283 100644 --- a/src/components/ClaudeCodeSession.tsx +++ b/src/components/ClaudeCodeSession.tsx @@ -1,13 +1,13 @@ import React, { useState, useEffect, useRef, useMemo } from "react"; import { motion, AnimatePresence } from "framer-motion"; -import { +import { Copy, ChevronDown, GitBranch, ChevronUp, X, Hash, - Wrench + Wrench, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -17,18 +17,32 @@ import { api, type Session } from "@/lib/api"; import { cn } from "@/lib/utils"; import { listen, type UnlistenFn } from "@tauri-apps/api/event"; import { StreamMessage } from "./StreamMessage"; -import { FloatingPromptInput, type FloatingPromptInputRef } from "./FloatingPromptInput"; +import { + FloatingPromptInput, + type FloatingPromptInputRef, +} from "./FloatingPromptInput"; import { ErrorBoundary } from "./ErrorBoundary"; import { TimelineNavigator } from "./TimelineNavigator"; import { CheckpointSettings } from "./CheckpointSettings"; import { SlashCommandsManager } from "./SlashCommandsManager"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; import { TooltipProvider, TooltipSimple } from "@/components/ui/tooltip-modern"; import { SplitPane } from "@/components/ui/split-pane"; import { WebviewPreview } from "./WebviewPreview"; import type { ClaudeStreamMessage } from "./AgentExecution"; import { useVirtualizer } from "@tanstack/react-virtual"; -import { useTrackEvent, useComponentMetrics, useWorkflowTracking } from "@/hooks"; +import { + useTrackEvent, + useComponentMetrics, + useWorkflowTracking, +} from "@/hooks"; import { SessionPersistenceService } from "@/services/sessionPersistence"; interface ClaudeCodeSessionProps { @@ -64,7 +78,7 @@ interface ClaudeCodeSessionProps { /** * ClaudeCodeSession component for interactive Claude Code sessions - * + * * @example * setView('projects')} /> */ @@ -75,7 +89,9 @@ export const ClaudeCodeSession: React.FC = ({ onStreamingChange, onProjectPathChange, }) => { - const [projectPath] = useState(initialProjectPath || session?.project_path || ""); + const [projectPath] = useState( + initialProjectPath || session?.project_path || "", + ); const [messages, setMessages] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); @@ -83,38 +99,46 @@ export const ClaudeCodeSession: React.FC = ({ const [copyPopoverOpen, setCopyPopoverOpen] = useState(false); const [isFirstPrompt, setIsFirstPrompt] = useState(!session); const [totalTokens, setTotalTokens] = useState(0); - const [extractedSessionInfo, setExtractedSessionInfo] = useState<{ sessionId: string; projectId: string } | null>(null); + const [extractedSessionInfo, setExtractedSessionInfo] = useState<{ + sessionId: string; + projectId: string; + } | null>(null); const [claudeSessionId, setClaudeSessionId] = useState(null); const [showTimeline, setShowTimeline] = useState(false); const [timelineVersion, setTimelineVersion] = useState(0); const [showSettings, setShowSettings] = useState(false); const [showForkDialog, setShowForkDialog] = useState(false); - const [showSlashCommandsSettings, setShowSlashCommandsSettings] = useState(false); + const [showSlashCommandsSettings, setShowSlashCommandsSettings] = + useState(false); const [forkCheckpointId, setForkCheckpointId] = useState(null); const [forkSessionName, setForkSessionName] = useState(""); - + // Queued prompts state - const [queuedPrompts, setQueuedPrompts] = useState>([]); - + const [queuedPrompts, setQueuedPrompts] = useState< + Array<{ id: string; prompt: string; model: "sonnet" | "opus" }> + >([]); + // New state for preview feature const [showPreview, setShowPreview] = useState(false); const [previewUrl, setPreviewUrl] = useState(""); const [showPreviewPrompt, setShowPreviewPrompt] = useState(false); const [splitPosition, setSplitPosition] = useState(50); const [isPreviewMaximized, setIsPreviewMaximized] = useState(false); - + // Add collapsed state for queued prompts const [queuedPromptsCollapsed, setQueuedPromptsCollapsed] = useState(false); - + const parentRef = useRef(null); const unlistenRefs = useRef([]); const hasActiveSessionRef = useRef(false); const floatingPromptRef = useRef(null); - const queuedPromptsRef = useRef>([]); + const queuedPromptsRef = useRef< + Array<{ id: string; prompt: string; model: "sonnet" | "opus" }> + >([]); const isMountedRef = useRef(true); const isListeningRef = useRef(false); const sessionStartTime = useRef(Date.now()); - + // Session metrics state for enhanced analytics const sessionMetrics = useRef({ firstMessageTime: null as number | null, @@ -135,17 +159,17 @@ export const ClaudeCodeSession: React.FC = ({ // Analytics tracking const trackEvent = useTrackEvent(); - useComponentMetrics('ClaudeCodeSession'); + useComponentMetrics("ClaudeCodeSession"); // const aiTracking = useAIInteractionTracking('sonnet'); // Default model - const workflowTracking = useWorkflowTracking('claude_session'); - + const workflowTracking = useWorkflowTracking("claude_session"); + // Call onProjectPathChange when component mounts with initial path useEffect(() => { if (onProjectPathChange && projectPath) { onProjectPathChange(projectPath); } }, []); // Only run on mount - + // Keep ref in sync with state useEffect(() => { queuedPromptsRef.current = queuedPrompts; @@ -178,7 +202,10 @@ export const ClaudeCodeSession: React.FC = ({ if (message.isMeta) return false; const msg = message.message; - if (!msg.content || (Array.isArray(msg.content) && msg.content.length === 0)) { + if ( + !msg.content || + (Array.isArray(msg.content) && msg.content.length === 0) + ) { return false; } @@ -195,17 +222,33 @@ export const ClaudeCodeSession: React.FC = ({ // Look for the matching tool_use in previous assistant messages for (let i = index - 1; i >= 0; i--) { const prevMsg = messages[i]; - if (prevMsg.type === 'assistant' && prevMsg.message?.content && Array.isArray(prevMsg.message.content)) { - const toolUse = prevMsg.message.content.find((c: any) => - c.type === 'tool_use' && c.id === content.tool_use_id + if ( + prevMsg.type === "assistant" && + prevMsg.message?.content && + Array.isArray(prevMsg.message.content) + ) { + const toolUse = prevMsg.message.content.find( + (c: any) => + c.type === "tool_use" && c.id === content.tool_use_id, ); if (toolUse) { const toolName = toolUse.name?.toLowerCase(); const toolsWithWidgets = [ - 'task', 'edit', 'multiedit', 'todowrite', 'ls', 'read', - 'glob', 'bash', 'write', 'grep' + "task", + "edit", + "multiedit", + "todowrite", + "ls", + "read", + "glob", + "bash", + "write", + "grep", ]; - if (toolsWithWidgets.includes(toolName) || toolUse.name?.startsWith('mcp__')) { + if ( + toolsWithWidgets.includes(toolName) || + toolUse.name?.startsWith("mcp__") + ) { willBeSkipped = true; } break; @@ -237,22 +280,29 @@ export const ClaudeCodeSession: React.FC = ({ // Debug logging useEffect(() => { - console.log('[ClaudeCodeSession] State update:', { + console.log("[ClaudeCodeSession] State update:", { projectPath, session, extractedSessionInfo, effectiveSession, messagesCount: messages.length, - isLoading + isLoading, }); - }, [projectPath, session, extractedSessionInfo, effectiveSession, messages.length, isLoading]); + }, [ + projectPath, + session, + extractedSessionInfo, + effectiveSession, + messages.length, + isLoading, + ]); // Load session history if resuming useEffect(() => { if (session) { // Set the claudeSessionId immediately when we have a session setClaudeSessionId(session.id); - + // Load session history first, then check for active session const initializeSession = async () => { await loadSessionHistory(); @@ -261,7 +311,7 @@ export const ClaudeCodeSession: React.FC = ({ await checkForActiveSession(); } }; - + initializeSession(); } }, [session]); // Remove hasLoadedSession dependency to ensure it runs on mount @@ -274,7 +324,10 @@ export const ClaudeCodeSession: React.FC = ({ // Auto-scroll to bottom when new messages arrive useEffect(() => { if (displayableMessages.length > 0) { - rowVirtualizer.scrollToIndex(displayableMessages.length - 1, { align: 'end', behavior: 'smooth' }); + rowVirtualizer.scrollToIndex(displayableMessages.length - 1, { + align: "end", + behavior: "smooth", + }); } }, [displayableMessages.length, rowVirtualizer]); @@ -282,7 +335,11 @@ export const ClaudeCodeSession: React.FC = ({ useEffect(() => { const tokens = messages.reduce((total, msg) => { if (msg.message?.usage) { - return total + msg.message.usage.input_tokens + msg.message.usage.output_tokens; + return ( + total + + msg.message.usage.input_tokens + + msg.message.usage.output_tokens + ); } if (msg.usage) { return total + msg.usage.input_tokens + msg.usage.output_tokens; @@ -294,39 +351,45 @@ export const ClaudeCodeSession: React.FC = ({ const loadSessionHistory = async () => { if (!session) return; - + try { setIsLoading(true); setError(null); - - const history = await api.loadSessionHistory(session.id, session.project_id); - + + const history = await api.loadSessionHistory( + session.id, + session.project_id, + ); + // Save session data for restoration if (history && history.length > 0) { SessionPersistenceService.saveSession( session.id, session.project_id, session.project_path, - history.length + history.length, ); } - + // Convert history to messages format - const loadedMessages: ClaudeStreamMessage[] = history.map(entry => ({ + const loadedMessages: ClaudeStreamMessage[] = history.map((entry) => ({ ...entry, - type: entry.type || "assistant" + type: entry.type || "assistant", })); - + setMessages(loadedMessages); - setRawJsonlOutput(history.map(h => JSON.stringify(h))); - + setRawJsonlOutput(history.map((h) => JSON.stringify(h))); + // After loading history, we're continuing a conversation setIsFirstPrompt(false); - + // Scroll to bottom after loading history setTimeout(() => { if (loadedMessages.length > 0) { - rowVirtualizer.scrollToIndex(loadedMessages.length - 1, { align: 'end', behavior: 'auto' }); + rowVirtualizer.scrollToIndex(loadedMessages.length - 1, { + align: "end", + behavior: "auto", + }); } }, 100); } catch (err) { @@ -343,84 +406,110 @@ export const ClaudeCodeSession: React.FC = ({ try { const activeSessions = await api.listRunningClaudeSessions(); const activeSession = activeSessions.find((s: any) => { - if ('process_type' in s && s.process_type && 'ClaudeSession' in s.process_type) { - return (s.process_type as any).ClaudeSession.session_id === session.id; + if ( + "process_type" in s && + s.process_type && + "ClaudeSession" in s.process_type + ) { + return ( + (s.process_type as any).ClaudeSession.session_id === session.id + ); } return false; }); - + if (activeSession) { // Session is still active, reconnect to its stream - console.log('[ClaudeCodeSession] Found active session, reconnecting:', session.id); + console.log( + "[ClaudeCodeSession] Found active session, reconnecting:", + session.id, + ); // IMPORTANT: Set claudeSessionId before reconnecting setClaudeSessionId(session.id); - + // Don't add buffered messages here - they've already been loaded by loadSessionHistory // Just set up listeners for new messages - + // Set up listeners for the active session reconnectToSession(session.id); } } catch (err) { - console.error('Failed to check for active sessions:', err); + console.error("Failed to check for active sessions:", err); } } }; const reconnectToSession = async (sessionId: string) => { - console.log('[ClaudeCodeSession] Reconnecting to session:', sessionId); - + console.log("[ClaudeCodeSession] Reconnecting to session:", sessionId); + // Prevent duplicate listeners if (isListeningRef.current) { - console.log('[ClaudeCodeSession] Already listening to session, skipping reconnect'); + console.log( + "[ClaudeCodeSession] Already listening to session, skipping reconnect", + ); return; } - + // Clean up previous listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // IMPORTANT: Set the session ID before setting up listeners setClaudeSessionId(sessionId); - + // Mark as listening isListeningRef.current = true; - + // Set up session-specific listeners - const outputUnlisten = await listen(`claude-output:${sessionId}`, async (event) => { - try { - console.log('[ClaudeCodeSession] Received claude-output on reconnect:', event.payload); - - if (!isMountedRef.current) return; - - // Store raw JSONL - setRawJsonlOutput(prev => [...prev, event.payload]); - - // Parse and display - const message = JSON.parse(event.payload) as ClaudeStreamMessage; - setMessages(prev => [...prev, message]); - } catch (err) { - console.error("Failed to parse message:", err, event.payload); - } - }); + const outputUnlisten = await listen( + `claude-output:${sessionId}`, + async (event) => { + try { + console.log( + "[ClaudeCodeSession] Received claude-output on reconnect:", + event.payload, + ); + + if (!isMountedRef.current) return; + + // Store raw JSONL + setRawJsonlOutput((prev) => [...prev, event.payload]); + + // Parse and display + const message = JSON.parse(event.payload) as ClaudeStreamMessage; + setMessages((prev) => [...prev, message]); + } catch (err) { + console.error("Failed to parse message:", err, event.payload); + } + }, + ); - const errorUnlisten = await listen(`claude-error:${sessionId}`, (event) => { - console.error("Claude error:", event.payload); - if (isMountedRef.current) { - setError(event.payload); - } - }); + const errorUnlisten = await listen( + `claude-error:${sessionId}`, + (event) => { + console.error("Claude error:", event.payload); + if (isMountedRef.current) { + setError(event.payload); + } + }, + ); - const completeUnlisten = await listen(`claude-complete:${sessionId}`, async (event) => { - console.log('[ClaudeCodeSession] Received claude-complete on reconnect:', event.payload); - if (isMountedRef.current) { - setIsLoading(false); - hasActiveSessionRef.current = false; - } - }); + const completeUnlisten = await listen( + `claude-complete:${sessionId}`, + async (event) => { + console.log( + "[ClaudeCodeSession] Received claude-complete on reconnect:", + event.payload, + ); + if (isMountedRef.current) { + setIsLoading(false); + hasActiveSessionRef.current = false; + } + }, + ); unlistenRefs.current = [outputUnlisten, errorUnlisten, completeUnlisten]; - + // Mark as loading to show the session is active if (isMountedRef.current) { setIsLoading(true); @@ -431,8 +520,14 @@ export const ClaudeCodeSession: React.FC = ({ // Project path selection handled by parent tab controls const handleSendPrompt = async (prompt: string, model: "sonnet" | "opus") => { - console.log('[ClaudeCodeSession] handleSendPrompt called with:', { prompt, model, projectPath, claudeSessionId, effectiveSession }); - + console.log("[ClaudeCodeSession] handleSendPrompt called with:", { + prompt, + model, + projectPath, + claudeSessionId, + effectiveSession, + }); + if (!projectPath) { setError("Please select a project directory first"); return; @@ -443,9 +538,9 @@ export const ClaudeCodeSession: React.FC = ({ const newPrompt = { id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, prompt, - model + model, }; - setQueuedPrompts(prev => [...prev, newPrompt]); + setQueuedPrompts((prev) => [...prev, newPrompt]); return; } @@ -453,21 +548,21 @@ export const ClaudeCodeSession: React.FC = ({ setIsLoading(true); setError(null); hasActiveSessionRef.current = true; - + // For resuming sessions, ensure we have the session ID if (effectiveSession && !claudeSessionId) { setClaudeSessionId(effectiveSession.id); } - + // Only clean up and set up new listeners if not already listening if (!isListeningRef.current) { // Clean up previous listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // Mark as setting up listeners isListeningRef.current = true; - + // -------------------------------------------------------------------- // 1️⃣ Event Listener Setup Strategy // -------------------------------------------------------------------- @@ -481,150 +576,198 @@ export const ClaudeCodeSession: React.FC = ({ // generic ones to prevent duplicate handling. // -------------------------------------------------------------------- - console.log('[ClaudeCodeSession] Setting up generic event listeners first'); + console.log( + "[ClaudeCodeSession] Setting up generic event listeners first", + ); - let currentSessionId: string | null = claudeSessionId || effectiveSession?.id || null; + let currentSessionId: string | null = + claudeSessionId || effectiveSession?.id || null; // Helper to attach session-specific listeners **once we are sure** const attachSessionSpecificListeners = async (sid: string) => { - console.log('[ClaudeCodeSession] Attaching session-specific listeners for', sid); - - const specificOutputUnlisten = await listen(`claude-output:${sid}`, (evt) => { - handleStreamMessage(evt.payload); - }); - - const specificErrorUnlisten = await listen(`claude-error:${sid}`, (evt) => { - console.error('Claude error (scoped):', evt.payload); - setError(evt.payload); - }); - - const specificCompleteUnlisten = await listen(`claude-complete:${sid}`, (evt) => { - console.log('[ClaudeCodeSession] Received claude-complete (scoped):', evt.payload); - processComplete(evt.payload); - }); + console.log( + "[ClaudeCodeSession] Attaching session-specific listeners for", + sid, + ); + + const specificOutputUnlisten = await listen( + `claude-output:${sid}`, + (evt) => { + handleStreamMessage(evt.payload); + }, + ); + + const specificErrorUnlisten = await listen( + `claude-error:${sid}`, + (evt) => { + console.error("Claude error (scoped):", evt.payload); + setError(evt.payload); + }, + ); + + const specificCompleteUnlisten = await listen( + `claude-complete:${sid}`, + (evt) => { + console.log( + "[ClaudeCodeSession] Received claude-complete (scoped):", + evt.payload, + ); + processComplete(evt.payload); + }, + ); // Replace existing unlisten refs with these new ones (after cleaning up) unlistenRefs.current.forEach((u) => u()); - unlistenRefs.current = [specificOutputUnlisten, specificErrorUnlisten, specificCompleteUnlisten]; + unlistenRefs.current = [ + specificOutputUnlisten, + specificErrorUnlisten, + specificCompleteUnlisten, + ]; }; // Generic listeners (catch-all) - const genericOutputUnlisten = await listen('claude-output', async (event) => { - handleStreamMessage(event.payload); + const genericOutputUnlisten = await listen( + "claude-output", + async (event) => { + handleStreamMessage(event.payload); - // Attempt to extract session_id on the fly (for the very first init) - try { - const msg = JSON.parse(event.payload) as ClaudeStreamMessage; - if (msg.type === 'system' && msg.subtype === 'init' && msg.session_id) { - if (!currentSessionId || currentSessionId !== msg.session_id) { - console.log('[ClaudeCodeSession] Detected new session_id from generic listener:', msg.session_id); - currentSessionId = msg.session_id; - setClaudeSessionId(msg.session_id); - - // If we haven't extracted session info before, do it now - if (!extractedSessionInfo) { - const projectId = projectPath.replace(/[^a-zA-Z0-9]/g, '-'); - setExtractedSessionInfo({ sessionId: msg.session_id, projectId }); - - // Save session data for restoration - SessionPersistenceService.saveSession( + // Attempt to extract session_id on the fly (for the very first init) + try { + const msg = JSON.parse(event.payload) as ClaudeStreamMessage; + if ( + msg.type === "system" && + msg.subtype === "init" && + msg.session_id + ) { + if (!currentSessionId || currentSessionId !== msg.session_id) { + console.log( + "[ClaudeCodeSession] Detected new session_id from generic listener:", msg.session_id, - projectId, - projectPath, - messages.length ); - } + currentSessionId = msg.session_id; + setClaudeSessionId(msg.session_id); + + // If we haven't extracted session info before, do it now + if (!extractedSessionInfo) { + const projectId = projectPath.replace(/[^a-zA-Z0-9]/g, "-"); + setExtractedSessionInfo({ + sessionId: msg.session_id, + projectId, + }); + + // Save session data for restoration + SessionPersistenceService.saveSession( + msg.session_id, + projectId, + projectPath, + messages.length, + ); + } - // Switch to session-specific listeners - await attachSessionSpecificListeners(msg.session_id); + // Switch to session-specific listeners + await attachSessionSpecificListeners(msg.session_id); + } } + } catch { + /* ignore parse errors */ } - } catch { - /* ignore parse errors */ - } - }); + }, + ); // Helper to process any JSONL stream message string function handleStreamMessage(payload: string) { try { // Don't process if component unmounted if (!isMountedRef.current) return; - + // Store raw JSONL setRawJsonlOutput((prev) => [...prev, payload]); const message = JSON.parse(payload) as ClaudeStreamMessage; - + // Track enhanced tool execution - if (message.type === 'assistant' && message.message?.content) { - const toolUses = message.message.content.filter((c: any) => c.type === 'tool_use'); + if (message.type === "assistant" && message.message?.content) { + const toolUses = message.message.content.filter( + (c: any) => c.type === "tool_use", + ); toolUses.forEach((toolUse: any) => { // Increment tools executed counter sessionMetrics.current.toolsExecuted += 1; sessionMetrics.current.lastActivityTime = Date.now(); - + // Track file operations - const toolName = toolUse.name?.toLowerCase() || ''; - if (toolName.includes('create') || toolName.includes('write')) { + const toolName = toolUse.name?.toLowerCase() || ""; + if (toolName.includes("create") || toolName.includes("write")) { sessionMetrics.current.filesCreated += 1; - } else if (toolName.includes('edit') || toolName.includes('multiedit') || toolName.includes('search_replace')) { + } else if ( + toolName.includes("edit") || + toolName.includes("multiedit") || + toolName.includes("search_replace") + ) { sessionMetrics.current.filesModified += 1; - } else if (toolName.includes('delete')) { + } else if (toolName.includes("delete")) { sessionMetrics.current.filesDeleted += 1; } - + // Track tool start - we'll track completion when we get the result workflowTracking.trackStep(toolUse.name); }); } - + // Track tool results - if (message.type === 'user' && message.message?.content) { - const toolResults = message.message.content.filter((c: any) => c.type === 'tool_result'); + if (message.type === "user" && message.message?.content) { + const toolResults = message.message.content.filter( + (c: any) => c.type === "tool_result", + ); toolResults.forEach((result: any) => { const isError = result.is_error || false; // Note: We don't have execution time here, but we can track success/failure if (isError) { sessionMetrics.current.toolsFailed += 1; sessionMetrics.current.errorsEncountered += 1; - + trackEvent.enhancedError({ - error_type: 'tool_execution', - error_code: 'tool_failed', + error_type: "tool_execution", + error_code: "tool_failed", error_message: result.content, context: `Tool execution failed`, - user_action_before_error: 'executing_tool', + user_action_before_error: "executing_tool", recovery_attempted: false, recovery_successful: false, error_frequency: 1, - stack_trace_hash: undefined + stack_trace_hash: undefined, }); } }); } - + // Track code blocks generated - if (message.type === 'assistant' && message.message?.content) { - const codeBlocks = message.message.content.filter((c: any) => - c.type === 'text' && c.text?.includes('```') + if (message.type === "assistant" && message.message?.content) { + const codeBlocks = message.message.content.filter( + (c: any) => c.type === "text" && c.text?.includes("```"), ); if (codeBlocks.length > 0) { // Count code blocks in text content codeBlocks.forEach((block: any) => { const matches = (block.text.match(/```/g) || []).length; - sessionMetrics.current.codeBlocksGenerated += Math.floor(matches / 2); + sessionMetrics.current.codeBlocksGenerated += Math.floor( + matches / 2, + ); }); } } - + // Track errors in system messages - if (message.type === 'system' && (message.subtype === 'error' || message.error)) { + if ( + message.type === "system" && + (message.subtype === "error" || message.error) + ) { sessionMetrics.current.errorsEncountered += 1; } - + setMessages((prev) => [...prev, message]); } catch (err) { - console.error('Failed to parse message:', err, payload); + console.error("Failed to parse message:", err, payload); } } @@ -633,31 +776,36 @@ export const ClaudeCodeSession: React.FC = ({ setIsLoading(false); hasActiveSessionRef.current = false; isListeningRef.current = false; // Reset listening state - + // Track enhanced session stopped metrics when session completes if (effectiveSession && claudeSessionId) { - const sessionStartTimeValue = messages.length > 0 ? messages[0].timestamp || Date.now() : Date.now(); + const sessionStartTimeValue = + messages.length > 0 + ? messages[0].timestamp || Date.now() + : Date.now(); const duration = Date.now() - sessionStartTimeValue; const metrics = sessionMetrics.current; - const timeToFirstMessage = metrics.firstMessageTime - ? metrics.firstMessageTime - sessionStartTime.current + const timeToFirstMessage = metrics.firstMessageTime + ? metrics.firstMessageTime - sessionStartTime.current : undefined; const idleTime = Date.now() - metrics.lastActivityTime; - const avgResponseTime = metrics.toolExecutionTimes.length > 0 - ? metrics.toolExecutionTimes.reduce((a, b) => a + b, 0) / metrics.toolExecutionTimes.length - : undefined; - + const avgResponseTime = + metrics.toolExecutionTimes.length > 0 + ? metrics.toolExecutionTimes.reduce((a, b) => a + b, 0) / + metrics.toolExecutionTimes.length + : undefined; + trackEvent.enhancedSessionStopped({ // Basic metrics duration_ms: duration, messages_count: messages.length, - reason: success ? 'completed' : 'error', - + reason: success ? "completed" : "error", + // Timing metrics time_to_first_message_ms: timeToFirstMessage, average_response_time_ms: avgResponseTime, idle_time_ms: idleTime, - + // Interaction metrics prompts_sent: metrics.promptsSent, tools_executed: metrics.toolsExecuted, @@ -665,28 +813,29 @@ export const ClaudeCodeSession: React.FC = ({ files_created: metrics.filesCreated, files_modified: metrics.filesModified, files_deleted: metrics.filesDeleted, - + // Content metrics total_tokens_used: totalTokens, code_blocks_generated: metrics.codeBlocksGenerated, errors_encountered: metrics.errorsEncountered, - + // Session context - model: metrics.modelChanges.length > 0 - ? metrics.modelChanges[metrics.modelChanges.length - 1].to - : 'sonnet', + model: + metrics.modelChanges.length > 0 + ? metrics.modelChanges[metrics.modelChanges.length - 1].to + : "sonnet", has_checkpoints: metrics.checkpointCount > 0, checkpoint_count: metrics.checkpointCount, was_resumed: metrics.wasResumed, - + // Agent context (if applicable) agent_type: undefined, // TODO: Pass from agent execution agent_name: undefined, // TODO: Pass from agent execution agent_success: success, - + // Stop context - stop_source: 'completed', - final_state: success ? 'success' : 'failed', + stop_source: "completed", + final_state: success ? "success" : "failed", has_pending_prompts: queuedPrompts.length > 0, pending_prompts_count: queuedPrompts.length, }); @@ -697,7 +846,7 @@ export const ClaudeCodeSession: React.FC = ({ const settings = await api.getCheckpointSettings( effectiveSession.id, effectiveSession.project_id, - projectPath + projectPath, ); if (settings.auto_checkpoint_enabled) { @@ -705,13 +854,13 @@ export const ClaudeCodeSession: React.FC = ({ effectiveSession.id, effectiveSession.project_id, projectPath, - prompt + prompt, ); // Reload timeline to show new checkpoint setTimelineVersion((v) => v + 1); } } catch (err) { - console.error('Failed to check auto checkpoint:', err); + console.error("Failed to check auto checkpoint:", err); } } @@ -719,7 +868,7 @@ export const ClaudeCodeSession: React.FC = ({ if (queuedPromptsRef.current.length > 0) { const [nextPrompt, ...remainingPrompts] = queuedPromptsRef.current; setQueuedPrompts(remainingPrompts); - + // Small delay to ensure UI updates setTimeout(() => { handleSendPrompt(nextPrompt.prompt, nextPrompt.model); @@ -727,18 +876,31 @@ export const ClaudeCodeSession: React.FC = ({ } }; - const genericErrorUnlisten = await listen('claude-error', (evt) => { - console.error('Claude error:', evt.payload); - setError(evt.payload); - }); + const genericErrorUnlisten = await listen( + "claude-error", + (evt) => { + console.error("Claude error:", evt.payload); + setError(evt.payload); + }, + ); - const genericCompleteUnlisten = await listen('claude-complete', (evt) => { - console.log('[ClaudeCodeSession] Received claude-complete (generic):', evt.payload); - processComplete(evt.payload); - }); + const genericCompleteUnlisten = await listen( + "claude-complete", + (evt) => { + console.log( + "[ClaudeCodeSession] Received claude-complete (generic):", + evt.payload, + ); + processComplete(evt.payload); + }, + ); // Store the generic unlisteners for now; they may be replaced later. - unlistenRefs.current = [genericOutputUnlisten, genericErrorUnlisten, genericCompleteUnlisten]; + unlistenRefs.current = [ + genericOutputUnlisten, + genericErrorUnlisten, + genericCompleteUnlisten, + ]; // -------------------------------------------------------------------- // 2️⃣ Auto-checkpoint logic moved after listener setup (unchanged) @@ -751,63 +913,87 @@ export const ClaudeCodeSession: React.FC = ({ content: [ { type: "text", - text: prompt - } - ] - } + text: prompt, + }, + ], + }, }; - setMessages(prev => [...prev, userMessage]); - + setMessages((prev) => [...prev, userMessage]); + // Update session metrics sessionMetrics.current.promptsSent += 1; sessionMetrics.current.lastActivityTime = Date.now(); if (!sessionMetrics.current.firstMessageTime) { sessionMetrics.current.firstMessageTime = Date.now(); } - + // Track model changes - const lastModel = sessionMetrics.current.modelChanges.length > 0 - ? sessionMetrics.current.modelChanges[sessionMetrics.current.modelChanges.length - 1].to - : (sessionMetrics.current.wasResumed ? 'sonnet' : model); // Default to sonnet if resumed - + const lastModel = + sessionMetrics.current.modelChanges.length > 0 + ? sessionMetrics.current.modelChanges[ + sessionMetrics.current.modelChanges.length - 1 + ].to + : sessionMetrics.current.wasResumed + ? "sonnet" + : model; // Default to sonnet if resumed + if (lastModel !== model) { sessionMetrics.current.modelChanges.push({ from: lastModel, to: model, - timestamp: Date.now() + timestamp: Date.now(), }); } - + // Track enhanced prompt submission const codeBlockMatches = prompt.match(/```[\s\S]*?```/g) || []; const hasCode = codeBlockMatches.length > 0; - const conversationDepth = messages.filter(m => m.user_message).length; - const sessionAge = sessionStartTime.current ? Date.now() - sessionStartTime.current : 0; - const wordCount = prompt.split(/\s+/).filter(word => word.length > 0).length; - + const conversationDepth = messages.filter((m) => m.user_message).length; + const sessionAge = sessionStartTime.current + ? Date.now() - sessionStartTime.current + : 0; + const wordCount = prompt + .split(/\s+/) + .filter((word) => word.length > 0).length; + trackEvent.enhancedPromptSubmitted({ prompt_length: prompt.length, model: model, has_attachments: false, // TODO: Add attachment support when implemented - source: 'keyboard', // TODO: Track actual source (keyboard vs button) + source: "keyboard", // TODO: Track actual source (keyboard vs button) word_count: wordCount, conversation_depth: conversationDepth, - prompt_complexity: wordCount < 20 ? 'simple' : wordCount < 100 ? 'moderate' : 'complex', + prompt_complexity: + wordCount < 20 + ? "simple" + : wordCount < 100 + ? "moderate" + : "complex", contains_code: hasCode, - language_detected: hasCode ? codeBlockMatches?.[0]?.match(/```(\w+)/)?.[1] : undefined, - session_age_ms: sessionAge + language_detected: hasCode + ? codeBlockMatches?.[0]?.match(/```(\w+)/)?.[1] + : undefined, + session_age_ms: sessionAge, }); // Execute the appropriate command if (effectiveSession && !isFirstPrompt) { - console.log('[ClaudeCodeSession] Resuming session:', effectiveSession.id); + console.log( + "[ClaudeCodeSession] Resuming session:", + effectiveSession.id, + ); trackEvent.sessionResumed(effectiveSession.id); trackEvent.modelSelected(model); - await api.resumeClaudeCode(projectPath, effectiveSession.id, prompt, model); + await api.resumeClaudeCode( + projectPath, + effectiveSession.id, + prompt, + model, + ); } else { - console.log('[ClaudeCodeSession] Starting new session'); + console.log("[ClaudeCodeSession] Starting new session"); setIsFirstPrompt(false); - trackEvent.sessionCreated(model, 'prompt_input'); + trackEvent.sessionCreated(model, "prompt_input"); trackEvent.modelSelected(model); await api.executeClaudeCode(projectPath, prompt, model); } @@ -821,7 +1007,7 @@ export const ClaudeCodeSession: React.FC = ({ }; const handleCopyAsJsonl = async () => { - const jsonl = rawJsonlOutput.join('\n'); + const jsonl = rawJsonlOutput.join("\n"); await navigator.clipboard.writeText(jsonl); setCopyPopoverOpen(false); }; @@ -835,18 +1021,19 @@ export const ClaudeCodeSession: React.FC = ({ for (const msg of messages) { if (msg.type === "system" && msg.subtype === "init") { markdown += `## System Initialization\n\n`; - markdown += `- Session ID: \`${msg.session_id || 'N/A'}\`\n`; - markdown += `- Model: \`${msg.model || 'default'}\`\n`; + markdown += `- Session ID: \`${msg.session_id || "N/A"}\`\n`; + markdown += `- Model: \`${msg.model || "default"}\`\n`; if (msg.cwd) markdown += `- Working Directory: \`${msg.cwd}\`\n`; - if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(', ')}\n`; + if (msg.tools?.length) markdown += `- Tools: ${msg.tools.join(", ")}\n`; markdown += `\n`; } else if (msg.type === "assistant" && msg.message) { markdown += `## Assistant\n\n`; for (const content of msg.message.content || []) { if (content.type === "text") { - const textContent = typeof content.text === 'string' - ? content.text - : (content.text?.text || JSON.stringify(content.text || content)); + const textContent = + typeof content.text === "string" + ? content.text + : content.text?.text || JSON.stringify(content.text || content); markdown += `${textContent}\n\n`; } else if (content.type === "tool_use") { markdown += `### Tool: ${content.name}\n\n`; @@ -860,22 +1047,25 @@ export const ClaudeCodeSession: React.FC = ({ markdown += `## User\n\n`; for (const content of msg.message.content || []) { if (content.type === "text") { - const textContent = typeof content.text === 'string' - ? content.text - : (content.text?.text || JSON.stringify(content.text)); + const textContent = + typeof content.text === "string" + ? content.text + : content.text?.text || JSON.stringify(content.text); markdown += `${textContent}\n\n`; } else if (content.type === "tool_result") { markdown += `### Tool Result\n\n`; - let contentText = ''; - if (typeof content.content === 'string') { + let contentText = ""; + if (typeof content.content === "string") { contentText = content.content; - } else if (content.content && typeof content.content === 'object') { + } else if (content.content && typeof content.content === "object") { if (content.content.text) { contentText = content.content.text; } else if (Array.isArray(content.content)) { contentText = content.content - .map((c: any) => (typeof c === 'string' ? c : c.text || JSON.stringify(c))) - .join('\n'); + .map((c: any) => + typeof c === "string" ? c : c.text || JSON.stringify(c), + ) + .join("\n"); } else { contentText = JSON.stringify(content.content, null, 2); } @@ -904,7 +1094,7 @@ export const ClaudeCodeSession: React.FC = ({ // Ensure timeline reloads to highlight current checkpoint setTimelineVersion((v) => v + 1); }; - + const handleCheckpointCreated = () => { // Update checkpoint count in session metrics sessionMetrics.current.checkpointCount += 1; @@ -912,35 +1102,38 @@ export const ClaudeCodeSession: React.FC = ({ const handleCancelExecution = async () => { if (!claudeSessionId || !isLoading) return; - + try { - const sessionStartTime = messages.length > 0 ? messages[0].timestamp || Date.now() : Date.now(); + const sessionStartTime = + messages.length > 0 ? messages[0].timestamp || Date.now() : Date.now(); const duration = Date.now() - sessionStartTime; - + await api.cancelClaudeExecution(claudeSessionId); - + // Calculate metrics for enhanced analytics const metrics = sessionMetrics.current; - const timeToFirstMessage = metrics.firstMessageTime - ? metrics.firstMessageTime - sessionStartTime.current + const timeToFirstMessage = metrics.firstMessageTime + ? metrics.firstMessageTime - sessionStartTime.current : undefined; const idleTime = Date.now() - metrics.lastActivityTime; - const avgResponseTime = metrics.toolExecutionTimes.length > 0 - ? metrics.toolExecutionTimes.reduce((a, b) => a + b, 0) / metrics.toolExecutionTimes.length - : undefined; - + const avgResponseTime = + metrics.toolExecutionTimes.length > 0 + ? metrics.toolExecutionTimes.reduce((a, b) => a + b, 0) / + metrics.toolExecutionTimes.length + : undefined; + // Track enhanced session stopped trackEvent.enhancedSessionStopped({ // Basic metrics duration_ms: duration, messages_count: messages.length, - reason: 'user_stopped', - + reason: "user_stopped", + // Timing metrics time_to_first_message_ms: timeToFirstMessage, average_response_time_ms: avgResponseTime, idle_time_ms: idleTime, - + // Interaction metrics prompts_sent: metrics.promptsSent, tools_executed: metrics.toolsExecuted, @@ -948,70 +1141,71 @@ export const ClaudeCodeSession: React.FC = ({ files_created: metrics.filesCreated, files_modified: metrics.filesModified, files_deleted: metrics.filesDeleted, - + // Content metrics total_tokens_used: totalTokens, code_blocks_generated: metrics.codeBlocksGenerated, errors_encountered: metrics.errorsEncountered, - + // Session context - model: metrics.modelChanges.length > 0 - ? metrics.modelChanges[metrics.modelChanges.length - 1].to - : 'sonnet', // Default to sonnet + model: + metrics.modelChanges.length > 0 + ? metrics.modelChanges[metrics.modelChanges.length - 1].to + : "sonnet", // Default to sonnet has_checkpoints: metrics.checkpointCount > 0, checkpoint_count: metrics.checkpointCount, was_resumed: metrics.wasResumed, - + // Agent context (if applicable) agent_type: undefined, // TODO: Pass from agent execution agent_name: undefined, // TODO: Pass from agent execution agent_success: undefined, // TODO: Pass from agent execution - + // Stop context - stop_source: 'user_button', - final_state: 'cancelled', + stop_source: "user_button", + final_state: "cancelled", has_pending_prompts: queuedPrompts.length > 0, pending_prompts_count: queuedPrompts.length, }); - + // Clean up listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // Reset states setIsLoading(false); hasActiveSessionRef.current = false; isListeningRef.current = false; setError(null); - + // Clear queued prompts setQueuedPrompts([]); - + // Add a message indicating the session was cancelled const cancelMessage: ClaudeStreamMessage = { type: "system", subtype: "info", result: "Session cancelled by user", - timestamp: new Date().toISOString() + timestamp: new Date().toISOString(), }; - setMessages(prev => [...prev, cancelMessage]); + setMessages((prev) => [...prev, cancelMessage]); } catch (err) { console.error("Failed to cancel execution:", err); - + // Even if backend fails, we should update UI to reflect stopped state // Add error message but still stop the UI loading state const errorMessage: ClaudeStreamMessage = { type: "system", subtype: "error", - result: `Failed to cancel execution: ${err instanceof Error ? err.message : 'Unknown error'}. The process may still be running in the background.`, - timestamp: new Date().toISOString() + result: `Failed to cancel execution: ${err instanceof Error ? err.message : "Unknown error"}. The process may still be running in the background.`, + timestamp: new Date().toISOString(), }; - setMessages(prev => [...prev, errorMessage]); - + setMessages((prev) => [...prev, errorMessage]); + // Clean up listeners anyway - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // Reset states to allow user to continue setIsLoading(false); hasActiveSessionRef.current = false; @@ -1027,12 +1221,13 @@ export const ClaudeCodeSession: React.FC = ({ }; const handleConfirmFork = async () => { - if (!forkCheckpointId || !forkSessionName.trim() || !effectiveSession) return; - + if (!forkCheckpointId || !forkSessionName.trim() || !effectiveSession) + return; + try { setIsLoading(true); setError(null); - + const newSessionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; await api.forkFromCheckpoint( forkCheckpointId, @@ -1040,13 +1235,13 @@ export const ClaudeCodeSession: React.FC = ({ effectiveSession.project_id, projectPath, newSessionId, - forkSessionName + forkSessionName, ); - + // Open the new forked session // You would need to implement navigation to the new session console.log("Forked to new session:", newSessionId); - + setShowForkDialog(false); setForkCheckpointId(null); setForkSessionName(""); @@ -1073,7 +1268,7 @@ export const ClaudeCodeSession: React.FC = ({ }; const handlePreviewUrlChange = (url: string) => { - console.log('[ClaudeCodeSession] Preview URL changed to:', url); + console.log("[ClaudeCodeSession] Preview URL changed to:", url); setPreviewUrl(url); }; @@ -1088,50 +1283,57 @@ export const ClaudeCodeSession: React.FC = ({ // Cleanup event listeners and track mount state useEffect(() => { isMountedRef.current = true; - + return () => { - console.log('[ClaudeCodeSession] Component unmounting, cleaning up listeners'); + console.log( + "[ClaudeCodeSession] Component unmounting, cleaning up listeners", + ); isMountedRef.current = false; isListeningRef.current = false; - + // Track session completion with engagement metrics if (effectiveSession) { trackEvent.sessionCompleted(); - + // Track session engagement - const sessionDuration = sessionStartTime.current ? Date.now() - sessionStartTime.current : 0; - const messageCount = messages.filter(m => m.user_message).length; + const sessionDuration = sessionStartTime.current + ? Date.now() - sessionStartTime.current + : 0; + const messageCount = messages.filter((m) => m.user_message).length; const toolsUsed = new Set(); - messages.forEach(msg => { - if (msg.type === 'assistant' && msg.message?.content) { - const tools = msg.message.content.filter((c: any) => c.type === 'tool_use'); + messages.forEach((msg) => { + if (msg.type === "assistant" && msg.message?.content) { + const tools = msg.message.content.filter( + (c: any) => c.type === "tool_use", + ); tools.forEach((tool: any) => toolsUsed.add(tool.name)); } }); - + // Calculate engagement score (0-100) - const engagementScore = Math.min(100, - (messageCount * 10) + - (toolsUsed.size * 5) + - (sessionDuration > 300000 ? 20 : sessionDuration / 15000) // 5+ min session gets 20 points + const engagementScore = Math.min( + 100, + messageCount * 10 + + toolsUsed.size * 5 + + (sessionDuration > 300000 ? 20 : sessionDuration / 15000), // 5+ min session gets 20 points ); - + trackEvent.sessionEngagement({ session_duration_ms: sessionDuration, messages_sent: messageCount, tools_used: Array.from(toolsUsed), files_modified: 0, // TODO: Track file modifications - engagement_score: Math.round(engagementScore) + engagement_score: Math.round(engagementScore), }); } - + // Clean up listeners - unlistenRefs.current.forEach(unlisten => unlisten()); + unlistenRefs.current.forEach((unlisten) => unlisten()); unlistenRefs.current = []; - + // Clear checkpoint manager when session ends if (effectiveSession) { - api.clearCheckpointManager(effectiveSession.id).catch(err => { + api.clearCheckpointManager(effectiveSession.id).catch((err) => { console.error("Failed to clear checkpoint manager:", err); }); } @@ -1143,14 +1345,14 @@ export const ClaudeCodeSession: React.FC = ({ ref={parentRef} className="flex-1 overflow-y-auto relative pb-40" style={{ - contain: 'strict', + contain: "strict", }} >
@@ -1170,8 +1372,8 @@ export const ClaudeCodeSession: React.FC = ({ top: virtualItem.start, }} > - @@ -1213,7 +1415,7 @@ export const ClaudeCodeSession: React.FC = ({ if (showPreview && isPreviewMaximized) { return ( - = ({
+ {/* Main Content Area */} +
+ {showPreview ? ( + // Split pane layout when preview is active + + {projectPathInput} + {messagesList} +
+ } + right={ + + } + initialSplit={splitPosition} + onSplitChange={setSplitPosition} + minLeftWidth={400} + minRightWidth={400} + className="h-full" + /> + ) : ( + // Original layout when no preview +
+ {projectPathInput} + {messagesList} + + {isLoading && messages.length === 0 && ( +
+
+
+ + {session + ? "Loading session history..." + : "Initializing Claude Code..."} + +
+
+ )} +
+ )} +
- {/* Main Content Area */} -
- {showPreview ? ( - // Split pane layout when preview is active - - {projectPathInput} - {messagesList} -
- } - right={ - - } - initialSplit={splitPosition} - onSplitChange={setSplitPosition} - minLeftWidth={400} - minRightWidth={400} - className="h-full" - /> - ) : ( - // Original layout when no preview -
- {projectPathInput} - {messagesList} - - {isLoading && messages.length === 0 && ( -
-
-
- - {session ? "Loading session history..." : "Initializing Claude Code..."} - + {/* Floating Prompt Input - Always visible */} + + {/* Queued Prompts Display */} + + {queuedPrompts.length > 0 && ( + +
+
+
+ Queued Prompts ({queuedPrompts.length}) +
+ + + + + +
+ {!queuedPromptsCollapsed && + queuedPrompts.map((queuedPrompt, index) => ( + +
+
+ + #{index + 1} + + + {queuedPrompt.model === "opus" + ? "Opus" + : "Sonnet"} + +
+

+ {queuedPrompt.prompt} +

+
+ + + +
+ ))}
-
+ )} -
- )} -
+ - {/* Floating Prompt Input - Always visible */} - - {/* Queued Prompts Display */} - - {queuedPrompts.length > 0 && ( + {/* Navigation Arrows - positioned above prompt bar with spacing */} + {displayableMessages.length > 5 && ( -
-
-
- Queued Prompts ({queuedPrompts.length}) -
- - + + + - - -
- {!queuedPromptsCollapsed && queuedPrompts.map((queuedPrompt, index) => ( + + + + +
+ -
-
- #{index + 1} - - {queuedPrompt.model === "opus" ? "Opus" : "Sonnet"} - -
-

{queuedPrompt.prompt}

-
- { + // Use virtualizer to scroll to the last item + if (displayableMessages.length > 0) { + // Scroll to bottom of the container + const scrollElement = parentRef.current; + if (scrollElement) { + scrollElement.scrollTo({ + top: scrollElement.scrollHeight, + behavior: "smooth", + }); + } + } + }} + className="px-3 py-2 hover:bg-accent rounded-none" > - - + +
- ))} +
)} - - {/* Navigation Arrows - positioned above prompt bar with spacing */} - {displayableMessages.length > 5 && ( - -
- - - - - -
- - - + + + )} + {messages.length > 0 && ( + + + + + } - } - }} - className="px-3 py-2 hover:bg-accent rounded-none" - > - - - - -
- - )} - -
- - {effectiveSession && ( - + content={ +
+ + +
+ } + open={copyPopoverOpen} + onOpenChange={setCopyPopoverOpen} + side="top" + align="end" + /> + )} + = ({ - )} - {messages.length > 0 && ( - - - - -
- } - content={ -
- - -
- } - open={copyPopoverOpen} - onOpenChange={setCopyPopoverOpen} - side="top" - align="end" - /> - )} - + + } + /> +
+ + {/* Token Counter - positioned under the Send button */} + {totalTokens > 0 && ( +
+
+
- +
+ + + {totalTokens.toLocaleString()} + + tokens +
- - - } - /> -
- - {/* Token Counter - positioned under the Send button */} - {totalTokens > 0 && ( -
-
-
- -
- - {totalTokens.toLocaleString()} - tokens -
-
+
-
- )} - + )} + - {/* Timeline */} - - {showTimeline && effectiveSession && ( - -
- {/* Timeline Header */} -
-

Session Timeline

- -
- - {/* Timeline Content */} -
- -
-
-
- )} -
-
+ {/* Timeline */} + + {showTimeline && effectiveSession && ( + +
+ {/* Timeline Header */} +
+

Session Timeline

+ +
- {/* Fork Dialog */} - - - - Fork Session - - Create a new session branch from the selected checkpoint. - - - -
-
- - setForkSessionName(e.target.value)} - onKeyPress={(e) => { - if (e.key === "Enter" && !isLoading) { - handleConfirmFork(); - } - }} - /> -
-
- - - - - -
-
- - {/* Settings Dialog */} - {showSettings && effectiveSession && ( - - - setShowSettings(false)} - /> - - - )} + {/* Timeline Content */} +
+ +
+
+
+ )} +
+
- {/* Slash Commands Settings Dialog */} - {showSlashCommandsSettings && ( - - + {/* Fork Dialog */} + + - Slash Commands + Fork Session - Manage project-specific slash commands for {projectPath} + Create a new session branch from the selected checkpoint. -
- + +
+
+ + setForkSessionName(e.target.value)} + onKeyPress={(e) => { + if (e.key === "Enter" && !isLoading) { + handleConfirmFork(); + } + }} + /> +
+ + + + +
- )} + + {/* Settings Dialog */} + {showSettings && effectiveSession && ( + + + setShowSettings(false)} + /> + + + )} + + {/* Slash Commands Settings Dialog */} + {showSlashCommandsSettings && ( + + + + Slash Commands + + Manage project-specific slash commands for {projectPath} + + +
+ +
+
+
+ )}
); diff --git a/src/components/ClaudeFileEditor.tsx b/src/components/ClaudeFileEditor.tsx index 252e3e7bc..9d455de28 100644 --- a/src/components/ClaudeFileEditor.tsx +++ b/src/components/ClaudeFileEditor.tsx @@ -24,11 +24,11 @@ interface ClaudeFileEditorProps { /** * ClaudeFileEditor component for editing project-specific CLAUDE.md files - * + * * @example - * setEditingFile(null)} + * setEditingFile(null)} * /> */ export const ClaudeFileEditor: React.FC = ({ @@ -41,15 +41,18 @@ export const ClaudeFileEditor: React.FC = ({ const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); - const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null); - + const [toast, setToast] = useState<{ + message: string; + type: "success" | "error"; + } | null>(null); + const hasChanges = content !== originalContent; - + // Load the file content on mount useEffect(() => { loadFileContent(); }, [file.absolute_path]); - + const loadFileContent = async () => { try { setLoading(true); @@ -64,7 +67,7 @@ export const ClaudeFileEditor: React.FC = ({ setLoading(false); } }; - + const handleSave = async () => { try { setSaving(true); @@ -81,17 +84,17 @@ export const ClaudeFileEditor: React.FC = ({ setSaving(false); } }; - + const handleBack = () => { if (hasChanges) { const confirmLeave = window.confirm( - "You have unsaved changes. Are you sure you want to leave?" + "You have unsaved changes. Are you sure you want to leave?", ); if (!confirmLeave) return; } onBack(); }; - + return (
@@ -112,13 +115,15 @@ export const ClaudeFileEditor: React.FC = ({
-

{file.relative_path}

+

+ {file.relative_path} +

Edit project-specific Claude Code system prompt

- + - + {/* Error display */} {error && ( = ({ {error} )} - + {/* Editor */}
{loading ? ( @@ -151,7 +156,10 @@ export const ClaudeFileEditor: React.FC = ({
) : ( -
+
setContent(val || "")} @@ -163,7 +171,7 @@ export const ClaudeFileEditor: React.FC = ({ )}
- + {/* Toast Notification */} {toast && ( @@ -176,4 +184,4 @@ export const ClaudeFileEditor: React.FC = ({
); -}; \ No newline at end of file +}; diff --git a/src/components/ClaudeMemoriesDropdown.tsx b/src/components/ClaudeMemoriesDropdown.tsx index da4c8037e..c2c9cbb6a 100644 --- a/src/components/ClaudeMemoriesDropdown.tsx +++ b/src/components/ClaudeMemoriesDropdown.tsx @@ -24,7 +24,7 @@ interface ClaudeMemoriesDropdownProps { /** * ClaudeMemoriesDropdown component - Shows all CLAUDE.md files in a project - * + * * @example * = ({ const [files, setFiles] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - + // Load CLAUDE.md files when dropdown opens useEffect(() => { if (isOpen && files.length === 0) { loadClaudeMdFiles(); } }, [isOpen]); - + const loadClaudeMdFiles = async () => { try { setLoading(true); @@ -61,13 +61,13 @@ export const ClaudeMemoriesDropdown: React.FC = ({ setLoading(false); } }; - + const formatFileSize = (bytes: number): string => { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; }; - + return (
@@ -80,7 +80,9 @@ export const ClaudeMemoriesDropdown: React.FC = ({ CLAUDE.md Memories {files.length > 0 && !loading && ( - ({files.length}) + + ({files.length}) + )}
= ({ - + {/* Dropdown Content */} {isOpen && ( @@ -123,7 +125,9 @@ export const ClaudeMemoriesDropdown: React.FC = ({ className="flex items-center justify-between p-3 hover:bg-accent/50 transition-colors border-b border-border last:border-b-0" >
-

{file.relative_path}

+

+ {file.relative_path} +

{formatFileSize(file.size)} @@ -155,4 +159,4 @@ export const ClaudeMemoriesDropdown: React.FC = ({
); -}; \ No newline at end of file +}; diff --git a/src/components/ClaudeVersionSelector.tsx b/src/components/ClaudeVersionSelector.tsx index d78ac370c..129351aa4 100644 --- a/src/components/ClaudeVersionSelector.tsx +++ b/src/components/ClaudeVersionSelector.tsx @@ -1,8 +1,20 @@ import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { Badge } from "@/components/ui/badge"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { api, type ClaudeInstallation } from "@/lib/api"; import { cn } from "@/lib/utils"; @@ -42,7 +54,7 @@ interface ClaudeVersionSelectorProps { /** * ClaudeVersionSelector component for selecting Claude Code installations * Supports system installations and user preferences - * + * * @example * = ({ const [installations, setInstallations] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const [selectedInstallation, setSelectedInstallation] = useState(null); + const [selectedInstallation, setSelectedInstallation] = + useState(null); useEffect(() => { loadInstallations(); @@ -70,7 +83,7 @@ export const ClaudeVersionSelector: React.FC = ({ useEffect(() => { // Update selected installation when selectedPath changes if (selectedPath && installations.length > 0) { - const found = installations.find(i => i.path === selectedPath); + const found = installations.find((i) => i.path === selectedPath); if (found) { setSelectedInstallation(found); } @@ -83,10 +96,10 @@ export const ClaudeVersionSelector: React.FC = ({ setError(null); const foundInstallations = await api.listClaudeInstallations(); setInstallations(foundInstallations); - + // If we have a selected path, find and select it if (selectedPath) { - const found = foundInstallations.find(i => i.path === selectedPath); + const found = foundInstallations.find((i) => i.path === selectedPath); if (found) { setSelectedInstallation(found); } @@ -97,14 +110,18 @@ export const ClaudeVersionSelector: React.FC = ({ } } catch (err) { console.error("Failed to load Claude installations:", err); - setError(err instanceof Error ? err.message : "Failed to load Claude installations"); + setError( + err instanceof Error + ? err.message + : "Failed to load Claude installations", + ); } finally { setLoading(false); } }; const handleInstallationChange = (installationPath: string) => { - const installation = installations.find(i => i.path === installationPath); + const installation = installations.find((i) => i.path === installationPath); if (installation) { setSelectedInstallation(installation); onSelect(installation); @@ -189,8 +206,12 @@ export const ClaudeVersionSelector: React.FC = ({ ); } - const systemInstallations = installations.filter(i => i.installation_type === "System"); - const customInstallations = installations.filter(i => i.installation_type === "Custom"); + const systemInstallations = installations.filter( + (i) => i.installation_type === "System", + ); + const customInstallations = installations.filter( + (i) => i.installation_type === "Custom", + ); // Simplified mode - more streamlined UI if (simplified) { @@ -198,27 +219,43 @@ export const ClaudeVersionSelector: React.FC = ({
- +

Select which version of Claude to use

{selectedInstallation && ( - + {selectedInstallation.installation_type} )}
- - {selectedInstallation && (
- {selectedInstallation.path.split('/').pop() || selectedInstallation.path} + + {selectedInstallation.path.split("/").pop() || + selectedInstallation.path} + {selectedInstallation.version && ( - ({selectedInstallation.version}) + + ({selectedInstallation.version}) + )}
)} @@ -232,16 +269,27 @@ export const ClaudeVersionSelector: React.FC = ({ ) : ( <> {installations.map((installation) => ( - +
-
{installation.path}
+
+ {installation.path} +
- {installation.version || "Unknown version"} + + {installation.version || "Unknown version"} + {installation.source} - + {installation.installation_type}
@@ -253,12 +301,13 @@ export const ClaudeVersionSelector: React.FC = ({ )} - + {selectedInstallation && (
- Path: {selectedInstallation.path} + Path:{" "} + {selectedInstallation.path}
)} @@ -282,14 +331,22 @@ export const ClaudeVersionSelector: React.FC = ({ {/* Available Installations */}
- {selectedInstallation && (
{getInstallationIcon(selectedInstallation)} - {selectedInstallation.path} - + + {selectedInstallation.path} + + {selectedInstallation.installation_type}
@@ -299,15 +356,24 @@ export const ClaudeVersionSelector: React.FC = ({ {systemInstallations.length > 0 && ( <> -
System Installations
+
+ System Installations +
{systemInstallations.map((installation) => ( - +
{getInstallationIcon(installation)}
-
{installation.path}
+
+ {installation.path} +
- {installation.version || "Version unknown"} • {installation.source} + {installation.version || "Version unknown"} •{" "} + {installation.source}
@@ -321,15 +387,24 @@ export const ClaudeVersionSelector: React.FC = ({ {customInstallations.length > 0 && ( <> -
Custom Installations
+
+ Custom Installations +
{customInstallations.map((installation) => ( - +
{getInstallationIcon(installation)}
-
{installation.path}
+
+ {installation.path} +
- {installation.version || "Version unknown"} • {installation.source} + {installation.version || "Version unknown"} •{" "} + {installation.source}
@@ -349,15 +424,24 @@ export const ClaudeVersionSelector: React.FC = ({
Selected Installation - + {selectedInstallation.installation_type}
-
Path: {selectedInstallation.path}
-
Source: {selectedInstallation.source}
+
+ Path: {selectedInstallation.path} +
+
+ Source: {selectedInstallation.source} +
{selectedInstallation.version && ( -
Version: {selectedInstallation.version}
+
+ Version: {selectedInstallation.version} +
)}
@@ -365,8 +449,8 @@ export const ClaudeVersionSelector: React.FC = ({ {/* Save Button */} {showSaveButton && ( -
- + = ({
- + {/* Error display */} {error && ( = ({ {error} )} - + {/* Content */}
@@ -200,7 +219,12 @@ export const CreateAgent: React.FC = ({
- + = ({ className="h-9" />
- +
- + = ({ {/* Model Selection */}
- +
= ({ transition={{ duration: 0.15 }} className={cn( "flex-1 px-4 py-3 rounded-md border transition-all", - model === "sonnet" - ? "border-primary bg-primary/10 text-primary" - : "border-border hover:border-primary/50 hover:bg-accent" + model === "sonnet" + ? "border-primary bg-primary/10 text-primary" + : "border-border hover:border-primary/50 hover:bg-accent", )} >
- +
-
Claude 4 Sonnet
-
Faster, efficient for most tasks
+
+ Claude 4 Sonnet +
+
+ Faster, efficient for most tasks +
- + setModel("opus")} @@ -270,19 +306,27 @@ export const CreateAgent: React.FC = ({ transition={{ duration: 0.15 }} className={cn( "flex-1 px-4 py-3 rounded-md border transition-all", - model === "opus" - ? "border-primary bg-primary/10 text-primary" - : "border-border hover:border-primary/50 hover:bg-accent" + model === "opus" + ? "border-primary bg-primary/10 text-primary" + : "border-border hover:border-primary/50 hover:bg-accent", )} >
- +
-
Claude 4 Opus
-
More capable, better for complex tasks
+
+ Claude 4 Opus +
+
+ More capable, better for complex tasks +
@@ -294,7 +338,12 @@ export const CreateAgent: React.FC = ({

Configuration

- + = ({ className="h-9" />

- This will be used as the default task placeholder when executing the agent + This will be used as the default task placeholder when + executing the agent

@@ -317,7 +367,10 @@ export const CreateAgent: React.FC = ({ Define the behavior and capabilities of your Claude Code agent

-
+
setSystemPrompt(val || "")} @@ -330,7 +383,7 @@ export const CreateAgent: React.FC = ({
- + {/* Toast Notification */} {toast && ( @@ -354,4 +407,4 @@ export const CreateAgent: React.FC = ({ />
); -}; +}; diff --git a/src/components/CustomTitlebar.tsx b/src/components/CustomTitlebar.tsx index 3342959b0..03a7d2513 100644 --- a/src/components/CustomTitlebar.tsx +++ b/src/components/CustomTitlebar.tsx @@ -1,8 +1,19 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { motion } from 'framer-motion'; -import { Settings, Minus, Square, X, Bot, BarChart3, FileText, Network, Info, MoreVertical } from 'lucide-react'; -import { getCurrentWindow } from '@tauri-apps/api/window'; -import { TooltipProvider, TooltipSimple } from '@/components/ui/tooltip-modern'; +import React, { useState, useRef, useEffect } from "react"; +import { motion } from "framer-motion"; +import { + Settings, + Minus, + Square, + X, + Bot, + BarChart3, + FileText, + Network, + Info, + MoreVertical, +} from "lucide-react"; +import { getCurrentWindow } from "@tauri-apps/api/window"; +import { TooltipProvider, TooltipSimple } from "@/components/ui/tooltip-modern"; interface CustomTitlebarProps { onSettingsClick?: () => void; @@ -19,7 +30,7 @@ export const CustomTitlebar: React.FC = ({ onUsageClick, onClaudeClick, onMCPClick, - onInfoClick + onInfoClick, }) => { const [isHovered, setIsHovered] = useState(false); const [isDropdownOpen, setIsDropdownOpen] = useState(false); @@ -27,22 +38,25 @@ export const CustomTitlebar: React.FC = ({ useEffect(() => { const handleClickOutside = (event: MouseEvent) => { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { setIsDropdownOpen(false); } }; - document.addEventListener('mousedown', handleClickOutside); - return () => document.removeEventListener('mousedown', handleClickOutside); + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const handleMinimize = async () => { try { const window = getCurrentWindow(); await window.minimize(); - console.log('Window minimized successfully'); + console.log("Window minimized successfully"); } catch (error) { - console.error('Failed to minimize window:', error); + console.error("Failed to minimize window:", error); } }; @@ -52,13 +66,13 @@ export const CustomTitlebar: React.FC = ({ const isMaximized = await window.isMaximized(); if (isMaximized) { await window.unmaximize(); - console.log('Window unmaximized successfully'); + console.log("Window unmaximized successfully"); } else { await window.maximize(); - console.log('Window maximized successfully'); + console.log("Window maximized successfully"); } } catch (error) { - console.error('Failed to maximize/unmaximize window:', error); + console.error("Failed to maximize/unmaximize window:", error); } }; @@ -66,185 +80,194 @@ export const CustomTitlebar: React.FC = ({ try { const window = getCurrentWindow(); await window.close(); - console.log('Window closed successfully'); + console.log("Window closed successfully"); } catch (error) { - console.error('Failed to close window:', error); + console.error("Failed to close window:", error); } }; return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > - {/* Left side - macOS Traffic Light buttons */} -
-
- {/* Close button */} - - - {/* Minimize button */} - - - {/* Maximize button */} - +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {/* Left side - macOS Traffic Light buttons */} +
+
+ {/* Close button */} + + + {/* Minimize button */} + + + {/* Maximize button */} + +
-
- {/* Center - Title (hidden) */} - {/*
{title}
*/} - {/* Right side - Navigation icons with improved spacing */} -
- {/* Primary actions group */} -
- {onAgentsClick && ( - - - - - - )} - - {onUsageClick && ( - - - - - - )} -
+ {/* Right side - Navigation icons with improved spacing */} +
+ {/* Primary actions group */} +
+ {onAgentsClick && ( + + + + + + )} - {/* Visual separator */} -
- - {/* Secondary actions group */} -
- {onSettingsClick && ( - - - - - - )} - - {/* Dropdown menu for additional options */} -
- - setIsDropdownOpen(!isDropdownOpen)} - whileTap={{ scale: 0.97 }} - transition={{ duration: 0.15 }} - className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-1" - > - - - - - {isDropdownOpen && ( -
-
- {onClaudeClick && ( - - )} - - {onMCPClick && ( - - )} - - {onInfoClick && ( - - )} -
-
+ {onUsageClick && ( + + + + + + )} +
+ + {/* Visual separator */} +
+ + {/* Secondary actions group */} +
+ {onSettingsClick && ( + + + + + )} + + {/* Dropdown menu for additional options */} +
+ + setIsDropdownOpen(!isDropdownOpen)} + whileTap={{ scale: 0.97 }} + transition={{ duration: 0.15 }} + className="p-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-1" + > + + + + + {isDropdownOpen && ( +
+
+ {onClaudeClick && ( + + )} + + {onMCPClick && ( + + )} + + {onInfoClick && ( + + )} +
+
+ )} +
-
); }; diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index 591c99e37..cf5136a9c 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -16,7 +16,10 @@ interface ErrorBoundaryState { /** * Error Boundary component to catch and display React rendering errors */ -export class ErrorBoundary extends Component { +export class ErrorBoundary extends Component< + ErrorBoundaryProps, + ErrorBoundaryState +> { constructor(props: ErrorBoundaryProps) { super(props); this.state = { hasError: false, error: null }; @@ -51,7 +54,9 @@ export class ErrorBoundary extends Component
-

Something went wrong

+

+ Something went wrong +

An error occurred while rendering this component.

@@ -65,11 +70,7 @@ export class ErrorBoundary extends Component )} -
@@ -82,4 +83,4 @@ export class ErrorBoundary extends Component = ({ - isExecuting, - onStop, +export const ExecutionControlBar: React.FC = ({ + isExecuting, + onStop, totalTokens = 0, elapsedTime = 0, - className + className, }) => { // Format elapsed time const formatTime = (seconds: number) => { @@ -53,7 +53,7 @@ export const ExecutionControlBar: React.FC = ({ "fixed bottom-6 left-1/2 -translate-x-1/2 z-50", "bg-background/95 backdrop-blur-md border rounded-full shadow-lg", "px-6 py-3 flex items-center gap-4", - className + className, )} > {/* Rotating symbol indicator */} @@ -99,4 +99,4 @@ export const ExecutionControlBar: React.FC = ({ )} ); -}; \ No newline at end of file +}; diff --git a/src/components/FilePicker.optimized.tsx b/src/components/FilePicker.optimized.tsx index 4169e79b9..a6490565a 100644 --- a/src/components/FilePicker.optimized.tsx +++ b/src/components/FilePicker.optimized.tsx @@ -1,18 +1,24 @@ -import React, { useState, useEffect, useRef, useCallback, useMemo } from "react"; +import React, { + useState, + useEffect, + useRef, + useCallback, + useMemo, +} from "react"; import { motion } from "framer-motion"; import { useVirtualizer } from "@tanstack/react-virtual"; import { Button } from "@/components/ui/button"; import { api } from "@/lib/api"; -import { - X, - Folder, - File, +import { + X, + Folder, + File, ArrowLeft, FileCode, FileText, FileImage, Search, - ChevronRight + ChevronRight, } from "lucide-react"; import type { FileEntry } from "@/lib/api"; import { cn } from "@/lib/utils"; @@ -33,33 +39,33 @@ interface FilePickerProps { // Memoized file icon selector const getFileIcon = (entry: FileEntry) => { if (entry.is_directory) return Folder; - - const ext = entry.name.split('.').pop()?.toLowerCase(); + + const ext = entry.name.split(".").pop()?.toLowerCase(); switch (ext) { - case 'js': - case 'jsx': - case 'ts': - case 'tsx': - case 'py': - case 'java': - case 'cpp': - case 'c': - case 'go': - case 'rs': + case "js": + case "jsx": + case "ts": + case "tsx": + case "py": + case "java": + case "cpp": + case "c": + case "go": + case "rs": return FileCode; - case 'md': - case 'txt': - case 'json': - case 'xml': - case 'yaml': - case 'yml': + case "md": + case "txt": + case "json": + case "xml": + case "yaml": + case "yml": return FileText; - case 'png': - case 'jpg': - case 'jpeg': - case 'gif': - case 'svg': - case 'webp': + case "png": + case "jpg": + case "jpeg": + case "gif": + case "svg": + case "webp": return FileImage; default: return File; @@ -67,350 +73,385 @@ const getFileIcon = (entry: FileEntry) => { }; const formatFileSize = (bytes: number): string => { - if (bytes === 0) return '0 B'; + if (bytes === 0) return "0 B"; const k = 1024; - const sizes = ['B', 'KB', 'MB', 'GB']; + const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`; }; -export const FilePicker: React.FC = React.memo(({ - basePath, - onSelect, - onClose, - initialQuery = "", - className, - allowDirectorySelection = false -}) => { - const [currentPath, setCurrentPath] = useState(basePath); - const [entries, setEntries] = useState([]); - const [searchQuery, setSearchQuery] = useState(initialQuery); - const [selectedIndex, setSelectedIndex] = useState(0); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - const searchInputRef = useRef(null); - const scrollContainerRef = useRef(null); - const searchDebounceRef = useRef(); - - // Filter and sort entries - const displayEntries = useMemo(() => { - const filtered = searchQuery.trim() - ? entries.filter(entry => - entry.name.toLowerCase().includes(searchQuery.toLowerCase()) - ) - : entries; - - return filtered.sort((a, b) => { - if (a.is_directory !== b.is_directory) { - return a.is_directory ? -1 : 1; - } - return a.name.localeCompare(b.name); +export const FilePicker: React.FC = React.memo( + ({ + basePath, + onSelect, + onClose, + initialQuery = "", + className, + allowDirectorySelection = false, + }) => { + const [currentPath, setCurrentPath] = useState(basePath); + const [entries, setEntries] = useState([]); + const [searchQuery, setSearchQuery] = useState(initialQuery); + const [selectedIndex, setSelectedIndex] = useState(0); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const searchInputRef = useRef(null); + const scrollContainerRef = useRef(null); + const searchDebounceRef = useRef(); + + // Filter and sort entries + const displayEntries = useMemo(() => { + const filtered = searchQuery.trim() + ? entries.filter((entry) => + entry.name.toLowerCase().includes(searchQuery.toLowerCase()), + ) + : entries; + + return filtered.sort((a, b) => { + if (a.is_directory !== b.is_directory) { + return a.is_directory ? -1 : 1; + } + return a.name.localeCompare(b.name); + }); + }, [entries, searchQuery]); + + // Virtual scrolling setup + const virtualizer = useVirtualizer({ + count: displayEntries.length, + getScrollElement: () => scrollContainerRef.current, + estimateSize: () => 32, // Height of each item + overscan: 10, // Number of items to render outside viewport }); - }, [entries, searchQuery]); - - // Virtual scrolling setup - const virtualizer = useVirtualizer({ - count: displayEntries.length, - getScrollElement: () => scrollContainerRef.current, - estimateSize: () => 32, // Height of each item - overscan: 10, // Number of items to render outside viewport - }); - - const virtualItems = virtualizer.getVirtualItems(); - - // Load directory contents - const loadDirectory = useCallback(async (path: string) => { - const cacheKey = path; - - // Check cache first - if (globalDirectoryCache.has(cacheKey)) { - setEntries(globalDirectoryCache.get(cacheKey)!); - return; - } - - setIsLoading(true); - setError(null); - - try { - const result = await api.listDirectoryContents(path); - globalDirectoryCache.set(cacheKey, result); - setEntries(result); - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to load directory'); - } finally { - setIsLoading(false); - } - }, []); - - // Search functionality - const performSearch = useCallback(async (query: string) => { - if (!query.trim()) { - loadDirectory(currentPath); - return; - } - - const cacheKey = `${currentPath}:${query}`; - - if (globalSearchCache.has(cacheKey)) { - setEntries(globalSearchCache.get(cacheKey)!); - return; - } - - setIsLoading(true); - setError(null); - - try { - const result = await api.searchFiles(currentPath, query); - globalSearchCache.set(cacheKey, result); - setEntries(result); - } catch (err) { - setError(err instanceof Error ? err.message : 'Search failed'); - } finally { - setIsLoading(false); - } - }, [currentPath, loadDirectory]); - - // Handle entry click - const handleEntryClick = useCallback((entry: FileEntry) => { - if (!entry.is_directory || allowDirectorySelection) { - onSelect(entry); - } - }, [onSelect, allowDirectorySelection]); - - // Handle entry double click - const handleEntryDoubleClick = useCallback((entry: FileEntry) => { - if (entry.is_directory) { - setCurrentPath(entry.path); - setSearchQuery(""); - setSelectedIndex(0); - } else { - onSelect(entry); - } - }, [onSelect]); - - // Keyboard navigation - const handleKeyDown = useCallback((e: React.KeyboardEvent) => { - if (displayEntries.length === 0) return; - - switch (e.key) { - case 'ArrowUp': - e.preventDefault(); - setSelectedIndex(prev => Math.max(0, prev - 1)); - break; - case 'ArrowDown': - e.preventDefault(); - setSelectedIndex(prev => Math.min(displayEntries.length - 1, prev + 1)); - break; - case 'Enter': - e.preventDefault(); - const selectedEntry = displayEntries[selectedIndex]; - if (selectedEntry) { - if (e.shiftKey || !selectedEntry.is_directory) { - handleEntryClick(selectedEntry); - } else { - handleEntryDoubleClick(selectedEntry); - } + + const virtualItems = virtualizer.getVirtualItems(); + + // Load directory contents + const loadDirectory = useCallback(async (path: string) => { + const cacheKey = path; + + // Check cache first + if (globalDirectoryCache.has(cacheKey)) { + setEntries(globalDirectoryCache.get(cacheKey)!); + return; + } + + setIsLoading(true); + setError(null); + + try { + const result = await api.listDirectoryContents(path); + globalDirectoryCache.set(cacheKey, result); + setEntries(result); + } catch (err) { + setError( + err instanceof Error ? err.message : "Failed to load directory", + ); + } finally { + setIsLoading(false); + } + }, []); + + // Search functionality + const performSearch = useCallback( + async (query: string) => { + if (!query.trim()) { + loadDirectory(currentPath); + return; + } + + const cacheKey = `${currentPath}:${query}`; + + if (globalSearchCache.has(cacheKey)) { + setEntries(globalSearchCache.get(cacheKey)!); + return; + } + + setIsLoading(true); + setError(null); + + try { + const result = await api.searchFiles(currentPath, query); + globalSearchCache.set(cacheKey, result); + setEntries(result); + } catch (err) { + setError(err instanceof Error ? err.message : "Search failed"); + } finally { + setIsLoading(false); + } + }, + [currentPath, loadDirectory], + ); + + // Handle entry click + const handleEntryClick = useCallback( + (entry: FileEntry) => { + if (!entry.is_directory || allowDirectorySelection) { + onSelect(entry); + } + }, + [onSelect, allowDirectorySelection], + ); + + // Handle entry double click + const handleEntryDoubleClick = useCallback( + (entry: FileEntry) => { + if (entry.is_directory) { + setCurrentPath(entry.path); + setSearchQuery(""); + setSelectedIndex(0); + } else { + onSelect(entry); } - break; - case 'Escape': - e.preventDefault(); - onClose(); - break; - } - }, [displayEntries, selectedIndex, handleEntryClick, handleEntryDoubleClick, onClose]); - - // Debounced search - useEffect(() => { - if (searchDebounceRef.current) { - clearTimeout(searchDebounceRef.current); - } - - searchDebounceRef.current = setTimeout(() => { - performSearch(searchQuery); - }, 300); - - return () => { + }, + [onSelect], + ); + + // Keyboard navigation + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (displayEntries.length === 0) return; + + switch (e.key) { + case "ArrowUp": + e.preventDefault(); + setSelectedIndex((prev) => Math.max(0, prev - 1)); + break; + case "ArrowDown": + e.preventDefault(); + setSelectedIndex((prev) => + Math.min(displayEntries.length - 1, prev + 1), + ); + break; + case "Enter": + e.preventDefault(); + const selectedEntry = displayEntries[selectedIndex]; + if (selectedEntry) { + if (e.shiftKey || !selectedEntry.is_directory) { + handleEntryClick(selectedEntry); + } else { + handleEntryDoubleClick(selectedEntry); + } + } + break; + case "Escape": + e.preventDefault(); + onClose(); + break; + } + }, + [ + displayEntries, + selectedIndex, + handleEntryClick, + handleEntryDoubleClick, + onClose, + ], + ); + + // Debounced search + useEffect(() => { if (searchDebounceRef.current) { clearTimeout(searchDebounceRef.current); } - }; - }, [searchQuery, performSearch]); - - // Load initial directory - useEffect(() => { - loadDirectory(currentPath); - }, [currentPath, loadDirectory]); - - // Focus search input on mount - useEffect(() => { - searchInputRef.current?.focus(); - }, []); - - // Scroll selected item into view - useEffect(() => { - const item = virtualizer.getVirtualItems().find( - vItem => vItem.index === selectedIndex - ); - if (item) { - virtualizer.scrollToIndex(selectedIndex, { align: 'center' }); - } - }, [selectedIndex, virtualizer]); - - return ( - - {/* Header */} -
- - -
- - setSearchQuery(e.target.value)} - placeholder="Search files..." - className="flex-1 bg-transparent outline-none text-sm" - /> -
- -
- - {/* Current path */} -
-
- {currentPath} -
-
+ searchDebounceRef.current = setTimeout(() => { + performSearch(searchQuery); + }, 300); - {/* File list with virtual scrolling */} -
- {isLoading && ( -
-
Loading...
-
- )} + return () => { + if (searchDebounceRef.current) { + clearTimeout(searchDebounceRef.current); + } + }; + }, [searchQuery, performSearch]); - {error && ( -
-
{error}
-
+ // Load initial directory + useEffect(() => { + loadDirectory(currentPath); + }, [currentPath, loadDirectory]); + + // Focus search input on mount + useEffect(() => { + searchInputRef.current?.focus(); + }, []); + + // Scroll selected item into view + useEffect(() => { + const item = virtualizer + .getVirtualItems() + .find((vItem) => vItem.index === selectedIndex); + if (item) { + virtualizer.scrollToIndex(selectedIndex, { align: "center" }); + } + }, [selectedIndex, virtualizer]); + + return ( + - - - {searchQuery.trim() ? 'No files found' : 'Empty directory'} - + onKeyDown={handleKeyDown} + > + {/* Header */} +
+ + +
+ + setSearchQuery(e.target.value)} + placeholder="Search files..." + className="flex-1 bg-transparent outline-none text-sm" + />
- )} - {displayEntries.length > 0 && ( -
- {virtualItems.map((virtualRow) => { - const entry = displayEntries[virtualRow.index]; - const Icon = getFileIcon(entry); - const isSelected = virtualRow.index === selectedIndex; - - return ( -
- -
- ); - })} + + +
+ + {/* Current path */} +
+
+ {currentPath}
- )} -
+
- {/* Footer */} -
-
- {displayEntries.length} {displayEntries.length === 1 ? 'item' : 'items'} + {/* File list with virtual scrolling */} +
+ {isLoading && ( +
+
Loading...
+
+ )} + + {error && ( +
+
{error}
+
+ )} + + {!isLoading && !error && displayEntries.length === 0 && ( +
+ + + {searchQuery.trim() ? "No files found" : "Empty directory"} + +
+ )} + + {displayEntries.length > 0 && ( +
+ {virtualItems.map((virtualRow) => { + const entry = displayEntries[virtualRow.index]; + const Icon = getFileIcon(entry); + const isSelected = virtualRow.index === selectedIndex; + + return ( +
+ +
+ ); + })} +
+ )}
- {allowDirectorySelection && ( + + {/* Footer */} +
- Shift+Enter to select directory + {displayEntries.length}{" "} + {displayEntries.length === 1 ? "item" : "items"}
- )} -
- - ); -}); \ No newline at end of file + {allowDirectorySelection && ( +
+ Shift+Enter to select directory +
+ )} +
+ + ); + }, +); diff --git a/src/components/FilePicker.tsx b/src/components/FilePicker.tsx index 3038df4e1..0ec093cc6 100644 --- a/src/components/FilePicker.tsx +++ b/src/components/FilePicker.tsx @@ -2,16 +2,16 @@ import React, { useState, useEffect, useRef } from "react"; import { motion } from "framer-motion"; import { Button } from "@/components/ui/button"; import { api } from "@/lib/api"; -import { - X, - Folder, - File, +import { + X, + Folder, + File, ArrowLeft, FileCode, FileText, FileImage, Search, - ChevronRight + ChevronRight, } from "lucide-react"; import type { FileEntry } from "@/lib/api"; import { cn } from "@/lib/utils"; @@ -53,40 +53,58 @@ interface FilePickerProps { // File icon mapping based on extension const getFileIcon = (entry: FileEntry) => { if (entry.is_directory) return Folder; - + const ext = entry.extension?.toLowerCase(); if (!ext) return File; - + // Code files - if (['ts', 'tsx', 'js', 'jsx', 'py', 'rs', 'go', 'java', 'cpp', 'c', 'h'].includes(ext)) { + if ( + [ + "ts", + "tsx", + "js", + "jsx", + "py", + "rs", + "go", + "java", + "cpp", + "c", + "h", + ].includes(ext) + ) { return FileCode; } - + // Text/Markdown files - if (['md', 'txt', 'json', 'yaml', 'yml', 'toml', 'xml', 'html', 'css'].includes(ext)) { + if ( + ["md", "txt", "json", "yaml", "yml", "toml", "xml", "html", "css"].includes( + ext, + ) + ) { return FileText; } - + // Image files - if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'].includes(ext)) { + if (["png", "jpg", "jpeg", "gif", "svg", "webp", "ico"].includes(ext)) { return FileImage; } - + return File; }; // Format file size to human readable const formatFileSize = (bytes: number): string => { - if (bytes === 0) return ''; + if (bytes === 0) return ""; const k = 1024; - const sizes = ['B', 'KB', 'MB', 'GB']; + const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`; }; /** * FilePicker component - File browser with fuzzy search - * + * * @example * = ({ className, }) => { const searchQuery = initialQuery; - + const [currentPath, setCurrentPath] = useState(basePath); - const [entries, setEntries] = useState(() => - searchQuery.trim() ? [] : globalDirectoryCache.get(basePath) || [] + const [entries, setEntries] = useState(() => + searchQuery.trim() ? [] : globalDirectoryCache.get(basePath) || [], ); const [searchResults, setSearchResults] = useState(() => { if (searchQuery.trim()) { @@ -126,17 +144,17 @@ export const FilePicker: React.FC = ({ } return globalDirectoryCache.has(basePath); }); - + const searchDebounceRef = useRef(null); const fileListRef = useRef(null); - + // Computed values const displayEntries = searchQuery.trim() ? searchResults : entries; const canGoBack = pathHistory.length > 1; - + // Get relative path for display - const relativePath = currentPath.startsWith(basePath) - ? currentPath.slice(basePath.length) || '/' + const relativePath = currentPath.startsWith(basePath) + ? currentPath.slice(basePath.length) || "/" : currentPath; // Load directory contents @@ -152,15 +170,18 @@ export const FilePicker: React.FC = ({ if (searchQuery.trim()) { const cacheKey = `${basePath}:${searchQuery}`; - + // Immediately show cached results if available if (globalSearchCache.has(cacheKey)) { - console.log('[FilePicker] Immediately showing cached search results for:', searchQuery); + console.log( + "[FilePicker] Immediately showing cached search results for:", + searchQuery, + ); setSearchResults(globalSearchCache.get(cacheKey) || []); setIsShowingCached(true); setError(null); } - + // Schedule fresh search after debounce searchDebounceRef.current = setTimeout(() => { performSearch(searchQuery); @@ -186,43 +207,51 @@ export const FilePicker: React.FC = ({ useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { const displayEntries = searchQuery.trim() ? searchResults : entries; - + switch (e.key) { - case 'Escape': + case "Escape": e.preventDefault(); onClose(); break; - - case 'Enter': + + case "Enter": e.preventDefault(); // Enter always selects the current item (file or directory) - if (displayEntries.length > 0 && selectedIndex < displayEntries.length) { + if ( + displayEntries.length > 0 && + selectedIndex < displayEntries.length + ) { onSelect(displayEntries[selectedIndex]); } break; - - case 'ArrowUp': + + case "ArrowUp": e.preventDefault(); - setSelectedIndex(prev => Math.max(0, prev - 1)); + setSelectedIndex((prev) => Math.max(0, prev - 1)); break; - - case 'ArrowDown': + + case "ArrowDown": e.preventDefault(); - setSelectedIndex(prev => Math.min(displayEntries.length - 1, prev + 1)); + setSelectedIndex((prev) => + Math.min(displayEntries.length - 1, prev + 1), + ); break; - - case 'ArrowRight': + + case "ArrowRight": e.preventDefault(); // Right arrow enters directories - if (displayEntries.length > 0 && selectedIndex < displayEntries.length) { + if ( + displayEntries.length > 0 && + selectedIndex < displayEntries.length + ) { const entry = displayEntries[selectedIndex]; if (entry.is_directory) { navigateToDirectory(entry.path); } } break; - - case 'ArrowLeft': + + case "ArrowLeft": e.preventDefault(); // Left arrow goes back to parent directory if (canGoBack) { @@ -232,27 +261,32 @@ export const FilePicker: React.FC = ({ } }; - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); }, [entries, searchResults, selectedIndex, searchQuery, canGoBack]); // Scroll selected item into view useEffect(() => { if (fileListRef.current) { - const selectedElement = fileListRef.current.querySelector(`[data-index="${selectedIndex}"]`); + const selectedElement = fileListRef.current.querySelector( + `[data-index="${selectedIndex}"]`, + ); if (selectedElement) { - selectedElement.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); + selectedElement.scrollIntoView({ + block: "nearest", + behavior: "smooth", + }); } } }, [selectedIndex]); const loadDirectory = async (path: string) => { try { - console.log('[FilePicker] Loading directory:', path); - + console.log("[FilePicker] Loading directory:", path); + // Check cache first and show immediately if (globalDirectoryCache.has(path)) { - console.log('[FilePicker] Showing cached contents for:', path); + console.log("[FilePicker] Showing cached contents for:", path); setEntries(globalDirectoryCache.get(path) || []); setIsShowingCached(true); setError(null); @@ -260,24 +294,30 @@ export const FilePicker: React.FC = ({ // Only show loading if we don't have cached data setIsLoading(true); } - + // Always fetch fresh data in background const contents = await api.listDirectoryContents(path); - console.log('[FilePicker] Loaded fresh contents:', contents.length, 'items'); - + console.log( + "[FilePicker] Loaded fresh contents:", + contents.length, + "items", + ); + // Cache the results globalDirectoryCache.set(path, contents); - + // Update with fresh data setEntries(contents); setIsShowingCached(false); setError(null); } catch (err) { - console.error('[FilePicker] Failed to load directory:', path, err); - console.error('[FilePicker] Error details:', err); + console.error("[FilePicker] Failed to load directory:", path, err); + console.error("[FilePicker] Error details:", err); // Only set error if we don't have cached data to show if (!globalDirectoryCache.has(path)) { - setError(err instanceof Error ? err.message : 'Failed to load directory'); + setError( + err instanceof Error ? err.message : "Failed to load directory", + ); } } finally { setIsLoading(false); @@ -286,14 +326,14 @@ export const FilePicker: React.FC = ({ const performSearch = async (query: string) => { try { - console.log('[FilePicker] Searching for:', query, 'in:', basePath); - + console.log("[FilePicker] Searching for:", query, "in:", basePath); + // Create cache key that includes both query and basePath const cacheKey = `${basePath}:${query}`; - + // Check cache first and show immediately if (globalSearchCache.has(cacheKey)) { - console.log('[FilePicker] Showing cached search results for:', query); + console.log("[FilePicker] Showing cached search results for:", query); setSearchResults(globalSearchCache.get(cacheKey) || []); setIsShowingCached(true); setError(null); @@ -301,24 +341,28 @@ export const FilePicker: React.FC = ({ // Only show loading if we don't have cached data setIsLoading(true); } - + // Always fetch fresh results in background const results = await api.searchFiles(basePath, query); - console.log('[FilePicker] Fresh search results:', results.length, 'items'); - + console.log( + "[FilePicker] Fresh search results:", + results.length, + "items", + ); + // Cache the results globalSearchCache.set(cacheKey, results); - + // Update with fresh results setSearchResults(results); setIsShowingCached(false); setError(null); } catch (err) { - console.error('[FilePicker] Search failed:', query, err); + console.error("[FilePicker] Search failed:", query, err); // Only set error if we don't have cached data to show const cacheKey = `${basePath}:${query}`; if (!globalSearchCache.has(cacheKey)) { - setError(err instanceof Error ? err.message : 'Search failed'); + setError(err instanceof Error ? err.message : "Search failed"); } } finally { setIsLoading(false); @@ -327,7 +371,7 @@ export const FilePicker: React.FC = ({ const navigateToDirectory = (path: string) => { setCurrentPath(path); - setPathHistory(prev => [...prev, path]); + setPathHistory((prev) => [...prev, path]); }; const navigateBack = () => { @@ -335,7 +379,7 @@ export const FilePicker: React.FC = ({ const newHistory = [...pathHistory]; newHistory.pop(); // Remove current const previousPath = newHistory[newHistory.length - 1]; - + // Don't go beyond the base path if (previousPath.startsWith(basePath) || previousPath === basePath) { setCurrentPath(previousPath); @@ -348,7 +392,7 @@ export const FilePicker: React.FC = ({ // Single click always selects (file or directory) onSelect(entry); }; - + const handleEntryDoubleClick = (entry: FileEntry) => { // Double click navigates into directories if (entry.is_directory) { @@ -366,7 +410,7 @@ export const FilePicker: React.FC = ({ "w-[500px] h-[400px]", "bg-background border border-border rounded-lg shadow-lg", "flex flex-col overflow-hidden", - className + className, )} > {/* Header */} @@ -423,7 +467,7 @@ export const FilePicker: React.FC = ({
- {searchQuery.trim() ? 'No files found' : 'Empty directory'} + {searchQuery.trim() ? "No files found" : "Empty directory"}
)} @@ -432,9 +476,9 @@ export const FilePicker: React.FC = ({
{displayEntries.map((entry, index) => { const Icon = getFileIcon(entry); - const isSearching = searchQuery.trim() !== ''; + const isSearching = searchQuery.trim() !== ""; const isSelected = index === selectedIndex; - + return ( @@ -489,4 +539,4 @@ export const FilePicker: React.FC = ({
); -}; \ No newline at end of file +}; diff --git a/src/components/FloatingPromptInput.tsx b/src/components/FloatingPromptInput.tsx index c3f5ea28c..ec3ca7dc0 100644 --- a/src/components/FloatingPromptInput.tsx +++ b/src/components/FloatingPromptInput.tsx @@ -12,13 +12,18 @@ import { Lightbulb, Cpu, Rocket, - } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Popover } from "@/components/ui/popover"; import { Textarea } from "@/components/ui/textarea"; -import { TooltipProvider, TooltipSimple, Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip-modern"; +import { + TooltipProvider, + TooltipSimple, + Tooltip, + TooltipTrigger, + TooltipContent, +} from "@/components/ui/tooltip-modern"; import { FilePicker } from "./FilePicker"; import { SlashCommandPicker } from "./SlashCommandPicker"; import { ImagePreview } from "./ImagePreview"; @@ -67,7 +72,12 @@ export interface FloatingPromptInputRef { /** * Thinking mode type definition */ -type ThinkingMode = "auto" | "think" | "think_hard" | "think_harder" | "ultrathink"; +type ThinkingMode = + | "auto" + | "think" + | "think_hard" + | "think_harder" + | "ultrathink"; /** * Thinking mode configuration @@ -91,7 +101,7 @@ const THINKING_MODES: ThinkingModeConfig[] = [ level: 0, icon: , color: "text-muted-foreground", - shortName: "A" + shortName: "A", }, { id: "think", @@ -101,7 +111,7 @@ const THINKING_MODES: ThinkingModeConfig[] = [ phrase: "think", icon: , color: "text-primary", - shortName: "T" + shortName: "T", }, { id: "think_hard", @@ -111,7 +121,7 @@ const THINKING_MODES: ThinkingModeConfig[] = [ phrase: "think hard", icon: , color: "text-primary", - shortName: "T+" + shortName: "T+", }, { id: "think_harder", @@ -121,7 +131,7 @@ const THINKING_MODES: ThinkingModeConfig[] = [ phrase: "think harder", icon: , color: "text-primary", - shortName: "T++" + shortName: "T++", }, { id: "ultrathink", @@ -131,19 +141,22 @@ const THINKING_MODES: ThinkingModeConfig[] = [ phrase: "ultrathink", icon: , color: "text-primary", - shortName: "Ultra" - } + shortName: "Ultra", + }, ]; /** * ThinkingModeIndicator component - Shows visual indicator bars for thinking level */ -const ThinkingModeIndicator: React.FC<{ level: number; color?: string }> = ({ level, color: _color }) => { +const ThinkingModeIndicator: React.FC<{ level: number; color?: string }> = ({ + level, + color: _color, +}) => { const getBarColor = (barIndex: number) => { if (barIndex > level) return "bg-muted"; return "bg-primary"; }; - + return (
{[1, 2, 3, 4].map((i) => ( @@ -152,7 +165,7 @@ const ThinkingModeIndicator: React.FC<{ level: number; color?: string }> = ({ le className={cn( "w-1 h-3 rounded-full transition-all duration-200", getBarColor(i), - i <= level && "shadow-sm" + i <= level && "shadow-sm", )} /> ))} @@ -176,7 +189,7 @@ const MODELS: Model[] = [ description: "Faster, efficient for most tasks", icon: , shortName: "S", - color: "text-primary" + color: "text-primary", }, { id: "opus", @@ -184,13 +197,13 @@ const MODELS: Model[] = [ description: "More capable, better for complex tasks", icon: , shortName: "O", - color: "text-primary" - } + color: "text-primary", + }, ]; /** * FloatingPromptInput component - Fixed position prompt input with model picker - * + * * @example * const promptRef = useRef(null); * , ) => { const [prompt, setPrompt] = useState(""); - const [selectedModel, setSelectedModel] = useState<"sonnet" | "opus">(defaultModel); - const [selectedThinkingMode, setSelectedThinkingMode] = useState("auto"); + const [selectedModel, setSelectedModel] = useState<"sonnet" | "opus">( + defaultModel, + ); + const [selectedThinkingMode, setSelectedThinkingMode] = + useState("auto"); const [isExpanded, setIsExpanded] = useState(false); const [modelPickerOpen, setModelPickerOpen] = useState(false); const [thinkingModePickerOpen, setThinkingModePickerOpen] = useState(false); @@ -236,108 +252,132 @@ const FloatingPromptInputInner = ( ref, () => ({ addImage: (imagePath: string) => { - setPrompt(currentPrompt => { + setPrompt((currentPrompt) => { const existingPaths = extractImagePaths(currentPrompt); if (existingPaths.includes(imagePath)) { return currentPrompt; // Image already added } // Wrap path in quotes if it contains spaces - const mention = imagePath.includes(' ') ? `@"${imagePath}"` : `@${imagePath}`; - const newPrompt = currentPrompt + (currentPrompt.endsWith(' ') || currentPrompt === '' ? '' : ' ') + mention + ' '; + const mention = imagePath.includes(" ") + ? `@"${imagePath}"` + : `@${imagePath}`; + const newPrompt = + currentPrompt + + (currentPrompt.endsWith(" ") || currentPrompt === "" ? "" : " ") + + mention + + " "; // Focus the textarea setTimeout(() => { - const target = isExpanded ? expandedTextareaRef.current : textareaRef.current; + const target = isExpanded + ? expandedTextareaRef.current + : textareaRef.current; target?.focus(); target?.setSelectionRange(newPrompt.length, newPrompt.length); }, 0); return newPrompt; }); - } + }, }), - [isExpanded] + [isExpanded], ); // Helper function to check if a file is an image const isImageFile = (path: string): boolean => { // Check if it's a data URL - if (path.startsWith('data:image/')) { + if (path.startsWith("data:image/")) { return true; } // Otherwise check file extension - const ext = path.split('.').pop()?.toLowerCase(); - return ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico', 'bmp'].includes(ext || ''); + const ext = path.split(".").pop()?.toLowerCase(); + return ["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"].includes( + ext || "", + ); }; // Extract image paths from prompt text const extractImagePaths = (text: string): string[] => { - console.log('[extractImagePaths] Input text length:', text.length); - + console.log("[extractImagePaths] Input text length:", text.length); + // Updated regex to handle both quoted and unquoted paths // Pattern 1: @"path with spaces or data URLs" - quoted paths // Pattern 2: @path - unquoted paths (continues until @ or end) const quotedRegex = /@"([^"]+)"/g; const unquotedRegex = /@([^@\n\s]+)/g; - + const pathsSet = new Set(); // Use Set to ensure uniqueness - + // First, extract quoted paths (including data URLs) let matches = Array.from(text.matchAll(quotedRegex)); - console.log('[extractImagePaths] Quoted matches:', matches.length); - + console.log("[extractImagePaths] Quoted matches:", matches.length); + for (const match of matches) { const path = match[1]; // No need to trim, quotes preserve exact path - console.log('[extractImagePaths] Processing quoted path:', path.startsWith('data:') ? 'data URL' : path); - + console.log( + "[extractImagePaths] Processing quoted path:", + path.startsWith("data:") ? "data URL" : path, + ); + // For data URLs, use as-is; for file paths, convert to absolute - const fullPath = path.startsWith('data:') - ? path - : (path.startsWith('/') ? path : (projectPath ? `${projectPath}/${path}` : path)); - + const fullPath = path.startsWith("data:") + ? path + : path.startsWith("/") + ? path + : projectPath + ? `${projectPath}/${path}` + : path; + if (isImageFile(fullPath)) { pathsSet.add(fullPath); } } - + // Remove quoted mentions from text to avoid double-matching - let textWithoutQuoted = text.replace(quotedRegex, ''); - + let textWithoutQuoted = text.replace(quotedRegex, ""); + // Then extract unquoted paths (typically file paths) matches = Array.from(textWithoutQuoted.matchAll(unquotedRegex)); - console.log('[extractImagePaths] Unquoted matches:', matches.length); - + console.log("[extractImagePaths] Unquoted matches:", matches.length); + for (const match of matches) { const path = match[1].trim(); // Skip if it looks like a data URL fragment (shouldn't happen with proper quoting) - if (path.includes('data:')) continue; - - console.log('[extractImagePaths] Processing unquoted path:', path); - + if (path.includes("data:")) continue; + + console.log("[extractImagePaths] Processing unquoted path:", path); + // Convert relative path to absolute if needed - const fullPath = path.startsWith('/') ? path : (projectPath ? `${projectPath}/${path}` : path); - + const fullPath = path.startsWith("/") + ? path + : projectPath + ? `${projectPath}/${path}` + : path; + if (isImageFile(fullPath)) { pathsSet.add(fullPath); } } const uniquePaths = Array.from(pathsSet); - console.log('[extractImagePaths] Final extracted paths (unique):', uniquePaths.length); + console.log( + "[extractImagePaths] Final extracted paths (unique):", + uniquePaths.length, + ); return uniquePaths; }; // Update embedded images when prompt changes useEffect(() => { - console.log('[useEffect] Prompt changed:', prompt); + console.log("[useEffect] Prompt changed:", prompt); const imagePaths = extractImagePaths(prompt); - console.log('[useEffect] Setting embeddedImages to:', imagePaths); + console.log("[useEffect] Setting embeddedImages to:", imagePaths); setEmbeddedImages(imagePaths); - + // Auto-resize on prompt change (handles paste, programmatic changes, etc.) if (textareaRef.current && !isExpanded) { - textareaRef.current.style.height = 'auto'; + textareaRef.current.style.height = "auto"; const scrollHeight = textareaRef.current.scrollHeight; const newHeight = Math.min(Math.max(scrollHeight, 48), 240); setTextareaHeight(newHeight); @@ -359,11 +399,11 @@ const FloatingPromptInputInner = ( const webview = getCurrentWebviewWindow(); unlistenDragDropRef.current = await webview.onDragDropEvent((event) => { - if (event.payload.type === 'enter' || event.payload.type === 'over') { + if (event.payload.type === "enter" || event.payload.type === "over") { setDragActive(true); - } else if (event.payload.type === 'leave') { + } else if (event.payload.type === "leave") { setDragActive(false); - } else if (event.payload.type === 'drop' && event.payload.paths) { + } else if (event.payload.type === "drop" && event.payload.paths) { setDragActive(false); const currentTime = Date.now(); @@ -378,26 +418,38 @@ const FloatingPromptInputInner = ( const imagePaths = droppedPaths.filter(isImageFile); if (imagePaths.length > 0) { - setPrompt(currentPrompt => { + setPrompt((currentPrompt) => { const existingPaths = extractImagePaths(currentPrompt); - const newPaths = imagePaths.filter(p => !existingPaths.includes(p)); + const newPaths = imagePaths.filter( + (p) => !existingPaths.includes(p), + ); if (newPaths.length === 0) { return currentPrompt; // All dropped images are already in the prompt } // Wrap paths with spaces in quotes for clarity - const mentionsToAdd = newPaths.map(p => { - // If path contains spaces, wrap in quotes - if (p.includes(' ')) { - return `@"${p}"`; - } - return `@${p}`; - }).join(' '); - const newPrompt = currentPrompt + (currentPrompt.endsWith(' ') || currentPrompt === '' ? '' : ' ') + mentionsToAdd + ' '; + const mentionsToAdd = newPaths + .map((p) => { + // If path contains spaces, wrap in quotes + if (p.includes(" ")) { + return `@"${p}"`; + } + return `@${p}`; + }) + .join(" "); + const newPrompt = + currentPrompt + + (currentPrompt.endsWith(" ") || currentPrompt === "" + ? "" + : " ") + + mentionsToAdd + + " "; setTimeout(() => { - const target = isExpanded ? expandedTextareaRef.current : textareaRef.current; + const target = isExpanded + ? expandedTextareaRef.current + : textareaRef.current; target?.focus(); target?.setSelectionRange(newPrompt.length, newPrompt.length); }, 0); @@ -408,7 +460,7 @@ const FloatingPromptInputInner = ( } }); } catch (error) { - console.error('Failed to set up Tauri drag-drop listener:', error); + console.error("Failed to set up Tauri drag-drop listener:", error); } }; @@ -435,13 +487,15 @@ const FloatingPromptInputInner = ( const handleSend = () => { if (prompt.trim() && !disabled) { let finalPrompt = prompt.trim(); - + // Append thinking phrase if not auto mode - const thinkingMode = THINKING_MODES.find(m => m.id === selectedThinkingMode); + const thinkingMode = THINKING_MODES.find( + (m) => m.id === selectedThinkingMode, + ); if (thinkingMode && thinkingMode.phrase) { finalPrompt = `${finalPrompt}.\n\n${thinkingMode.phrase}.`; } - + onSend(finalPrompt, selectedModel); setPrompt(""); setEmbeddedImages([]); @@ -452,11 +506,11 @@ const FloatingPromptInputInner = ( const handleTextChange = (e: React.ChangeEvent) => { const newValue = e.target.value; const newCursorPosition = e.target.selectionStart || 0; - + // Auto-resize textarea based on content if (textareaRef.current && !isExpanded) { // Reset height to auto to get the actual scrollHeight - textareaRef.current.style.height = 'auto'; + textareaRef.current.style.height = "auto"; const scrollHeight = textareaRef.current.scrollHeight; // Set min height to 48px and max to 240px (about 10 lines) const newHeight = Math.min(Math.max(scrollHeight, 48), 240); @@ -465,13 +519,17 @@ const FloatingPromptInputInner = ( } // Check if / was just typed at the beginning of input or after whitespace - if (newValue.length > prompt.length && newValue[newCursorPosition - 1] === '/') { + if ( + newValue.length > prompt.length && + newValue[newCursorPosition - 1] === "/" + ) { // Check if it's at the start or after whitespace - const isStartOfCommand = newCursorPosition === 1 || + const isStartOfCommand = + newCursorPosition === 1 || (newCursorPosition > 1 && /\s/.test(newValue[newCursorPosition - 2])); - + if (isStartOfCommand) { - console.log('[FloatingPromptInput] / detected for slash command'); + console.log("[FloatingPromptInput] / detected for slash command"); setShowSlashCommandPicker(true); setSlashCommandQuery(""); setCursorPosition(newCursorPosition); @@ -479,8 +537,15 @@ const FloatingPromptInputInner = ( } // Check if @ was just typed - if (projectPath?.trim() && newValue.length > prompt.length && newValue[newCursorPosition - 1] === '@') { - console.log('[FloatingPromptInput] @ detected, projectPath:', projectPath); + if ( + projectPath?.trim() && + newValue.length > prompt.length && + newValue[newCursorPosition - 1] === "@" + ) { + console.log( + "[FloatingPromptInput] @ detected, projectPath:", + projectPath, + ); setShowFilePicker(true); setFilePickerQuery(""); setCursorPosition(newCursorPosition); @@ -491,12 +556,12 @@ const FloatingPromptInputInner = ( // Find the / position before cursor let slashPosition = -1; for (let i = newCursorPosition - 1; i >= 0; i--) { - if (newValue[i] === '/') { + if (newValue[i] === "/") { slashPosition = i; break; } // Stop if we hit whitespace (new word) - if (newValue[i] === ' ' || newValue[i] === '\n') { + if (newValue[i] === " " || newValue[i] === "\n") { break; } } @@ -516,12 +581,12 @@ const FloatingPromptInputInner = ( // Find the @ position before cursor let atPosition = -1; for (let i = newCursorPosition - 1; i >= 0; i--) { - if (newValue[i] === '@') { + if (newValue[i] === "@") { atPosition = i; break; } // Stop if we hit whitespace (new word) - if (newValue[i] === ' ' || newValue[i] === '\n') { + if (newValue[i] === " " || newValue[i] === "\n") { break; } } @@ -545,19 +610,19 @@ const FloatingPromptInputInner = ( // Find the @ position before cursor let atPosition = -1; for (let i = cursorPosition - 1; i >= 0; i--) { - if (prompt[i] === '@') { + if (prompt[i] === "@") { atPosition = i; break; } // Stop if we hit whitespace (new word) - if (prompt[i] === ' ' || prompt[i] === '\n') { + if (prompt[i] === " " || prompt[i] === "\n") { break; } } if (atPosition === -1) { // @ not found, this shouldn't happen but handle gracefully - console.error('[FloatingPromptInput] @ position not found'); + console.error("[FloatingPromptInput] @ position not found"); return; } @@ -565,8 +630,8 @@ const FloatingPromptInputInner = ( const textarea = textareaRef.current; const beforeAt = prompt.substring(0, atPosition); const afterCursor = prompt.substring(cursorPosition); - const relativePath = entry.path.startsWith(projectPath || '') - ? entry.path.slice((projectPath || '').length + 1) + const relativePath = entry.path.startsWith(projectPath || "") + ? entry.path.slice((projectPath || "").length + 1) : entry.path; const newPrompt = `${beforeAt}@${relativePath} ${afterCursor}`; @@ -593,31 +658,33 @@ const FloatingPromptInputInner = ( }; const handleSlashCommandSelect = (command: SlashCommand) => { - const textarea = isExpanded ? expandedTextareaRef.current : textareaRef.current; + const textarea = isExpanded + ? expandedTextareaRef.current + : textareaRef.current; if (!textarea) return; // Find the / position before cursor let slashPosition = -1; for (let i = cursorPosition - 1; i >= 0; i--) { - if (prompt[i] === '/') { + if (prompt[i] === "/") { slashPosition = i; break; } // Stop if we hit whitespace (new word) - if (prompt[i] === ' ' || prompt[i] === '\n') { + if (prompt[i] === " " || prompt[i] === "\n") { break; } } if (slashPosition === -1) { - console.error('[FloatingPromptInput] / position not found'); + console.error("[FloatingPromptInput] / position not found"); return; } // Simply insert the command syntax const beforeSlash = prompt.substring(0, slashPosition); const afterCursor = prompt.substring(cursorPosition); - + if (command.accepts_arguments) { // Insert command with placeholder for arguments const newPrompt = `${beforeSlash}${command.full_command} `; @@ -628,7 +695,8 @@ const FloatingPromptInputInner = ( // Focus and position cursor after the command setTimeout(() => { textarea.focus(); - const newCursorPos = beforeSlash.length + command.full_command.length + 1; + const newCursorPos = + beforeSlash.length + command.full_command.length + 1; textarea.setSelectionRange(newCursorPos, newCursorPos); }, 0); } else { @@ -641,7 +709,8 @@ const FloatingPromptInputInner = ( // Focus and position cursor after the command setTimeout(() => { textarea.focus(); - const newCursorPos = beforeSlash.length + command.full_command.length + 1; + const newCursorPos = + beforeSlash.length + command.full_command.length + 1; textarea.setSelectionRange(newCursorPos, newCursorPos); }, 0); } @@ -652,20 +721,22 @@ const FloatingPromptInputInner = ( setSlashCommandQuery(""); // Return focus to textarea setTimeout(() => { - const textarea = isExpanded ? expandedTextareaRef.current : textareaRef.current; + const textarea = isExpanded + ? expandedTextareaRef.current + : textareaRef.current; textarea?.focus(); }, 0); }; const handleKeyDown = (e: React.KeyboardEvent) => { - if (showFilePicker && e.key === 'Escape') { + if (showFilePicker && e.key === "Escape") { e.preventDefault(); setShowFilePicker(false); setFilePickerQuery(""); return; } - if (showSlashCommandPicker && e.key === 'Escape') { + if (showSlashCommandPicker && e.key === "Escape") { e.preventDefault(); setShowSlashCommandPicker(false); setSlashCommandQuery(""); @@ -673,13 +744,19 @@ const FloatingPromptInputInner = ( } // Add keyboard shortcut for expanding - if (e.key === 'e' && (e.ctrlKey || e.metaKey) && e.shiftKey) { + if (e.key === "e" && (e.ctrlKey || e.metaKey) && e.shiftKey) { e.preventDefault(); setIsExpanded(true); return; } - if (e.key === "Enter" && !e.shiftKey && !isExpanded && !showFilePicker && !showSlashCommandPicker) { + if ( + e.key === "Enter" && + !e.shiftKey && + !isExpanded && + !showFilePicker && + !showSlashCommandPicker + ) { e.preventDefault(); handleSend(); } @@ -690,9 +767,9 @@ const FloatingPromptInputInner = ( if (!items) return; for (const item of items) { - if (item.type.startsWith('image/')) { + if (item.type.startsWith("image/")) { e.preventDefault(); - + // Get the image blob const blob = item.getAsFile(); if (!blob) continue; @@ -702,16 +779,24 @@ const FloatingPromptInputInner = ( const reader = new FileReader(); reader.onload = () => { const base64Data = reader.result as string; - + // Add the base64 data URL directly to the prompt - setPrompt(currentPrompt => { + setPrompt((currentPrompt) => { // Use the data URL directly as the image reference const mention = `@"${base64Data}"`; - const newPrompt = currentPrompt + (currentPrompt.endsWith(' ') || currentPrompt === '' ? '' : ' ') + mention + ' '; - + const newPrompt = + currentPrompt + + (currentPrompt.endsWith(" ") || currentPrompt === "" + ? "" + : " ") + + mention + + " "; + // Focus the textarea and move cursor to end setTimeout(() => { - const target = isExpanded ? expandedTextareaRef.current : textareaRef.current; + const target = isExpanded + ? expandedTextareaRef.current + : textareaRef.current; target?.focus(); target?.setSelectionRange(newPrompt.length, newPrompt.length); }, 0); @@ -719,10 +804,10 @@ const FloatingPromptInputInner = ( return newPrompt; }); }; - + reader.readAsDataURL(blob); } catch (error) { - console.error('Failed to paste image:', error); + console.error("Failed to paste image:", error); } } } @@ -745,282 +830,320 @@ const FloatingPromptInputInner = ( const handleRemoveImage = (index: number) => { // Remove the corresponding @mention from the prompt const imagePath = embeddedImages[index]; - + // For data URLs, we need to handle them specially since they're always quoted - if (imagePath.startsWith('data:')) { + if (imagePath.startsWith("data:")) { // Simply remove the exact quoted data URL const quotedPath = `@"${imagePath}"`; - const newPrompt = prompt.replace(quotedPath, '').trim(); + const newPrompt = prompt.replace(quotedPath, "").trim(); setPrompt(newPrompt); return; } - + // For file paths, use the original logic - const escapedPath = imagePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const escapedRelativePath = imagePath.replace(projectPath + '/', '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - + const escapedPath = imagePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const escapedRelativePath = imagePath + .replace(projectPath + "/", "") + .replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + // Create patterns for both quoted and unquoted mentions const patterns = [ // Quoted full path - new RegExp(`@"${escapedPath}"\\s?`, 'g'), + new RegExp(`@"${escapedPath}"\\s?`, "g"), // Unquoted full path - new RegExp(`@${escapedPath}\\s?`, 'g'), + new RegExp(`@${escapedPath}\\s?`, "g"), // Quoted relative path - new RegExp(`@"${escapedRelativePath}"\\s?`, 'g'), + new RegExp(`@"${escapedRelativePath}"\\s?`, "g"), // Unquoted relative path - new RegExp(`@${escapedRelativePath}\\s?`, 'g') + new RegExp(`@${escapedRelativePath}\\s?`, "g"), ]; let newPrompt = prompt; for (const pattern of patterns) { - newPrompt = newPrompt.replace(pattern, ''); + newPrompt = newPrompt.replace(pattern, ""); } setPrompt(newPrompt.trim()); }; - const selectedModelData = MODELS.find(m => m.id === selectedModel) || MODELS[0]; + const selectedModelData = + MODELS.find((m) => m.id === selectedModel) || MODELS[0]; return ( - <> - {/* Expanded Modal */} - - {isExpanded && ( - setIsExpanded(false)} - > + <> + {/* Expanded Modal */} + + {isExpanded && ( e.stopPropagation()} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} + className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-background/80 backdrop-blur-sm" + onClick={() => setIsExpanded(false)} > -
-

Compose your prompt

- - - - - -
+ +
+ +
- {/* Image previews in expanded mode */} - {embeddedImages.length > 0 && ( - 0 && ( + + )} + +