diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..12b811e --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +coverage/ +packages/mobile/.expo/ +infra/ +*.js diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 973f1a5..ed9d6f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: Lint and Type Check +name: CI Pipeline on: push: @@ -9,8 +9,8 @@ on: - main jobs: - lint: - name: Lint and Type Check + validate: + name: Lint, Type Check, and Test runs-on: ubuntu-latest steps: @@ -23,36 +23,29 @@ jobs: node-version: '20.x' cache: 'npm' - - name: Install dependencies + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.22' + cache: true + + - name: Install Node dependencies run: npm ci --legacy-peer-deps - - name: Build protocol package - run: | - cd packages/protocol - npm run build + - name: Build TypeScript packages + run: npm run build - name: Run ESLint run: npm run lint - continue-on-error: false - name: Check code formatting run: npm run format:check - continue-on-error: false - - - name: TypeScript check - Protocol - run: | - cd packages/protocol - npx tsc --noEmit - continue-on-error: false - - - name: TypeScript check - Relay Server - run: | - cd packages/relay-server - npx tsc --noEmit - continue-on-error: false - - - name: TypeScript check - VSCode Extension - run: | - cd packages/vscode-extension - npx tsc --noEmit - continue-on-error: false + + - name: TypeScript type checking + run: npm run typecheck + + - name: Run Vitest unit tests + run: npm run test + + - name: Run Go unit tests + run: npm run test:go diff --git a/.gitignore b/.gitignore index f826ec1..7eead02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,37 @@ node_modules/ -dist/ *.log .DS_Store .vscode-test/ *.vsix -.vscode .kiro # Test directory (centralized testing infrastructure) tests/ coverage/ -docs/ -MANUAL_TESTING_EDITOR_ADAPTERS.md -QUICK_START_TESTING.md +# DESIGN FILES +design/ +# Build artifacts +coverage/ +logs/ +*.tsbuildinfo -# ANDORID TESTING MANUALS -ANDROID_TESTING_GUIDE.md -ANDROID_TESTING_NOTES.md -ANDROID_TESTING_VERIFICATION.md -QUICK_START_ANDROID_TESTING.md -README_ANDROID_TESTING.md +# Go binaries +services/auth/bin/ +services/relay/bin/ -# IOS TESING MANUAL -IOS_TESTING_GUIDE.md -IOS_TESTING_VERIFICATION.md -QUICK_START_IOS_TESTING.md -PROJECT_ANALYSIS.md -TESTING_SPEC_SUMMARY.md -CI_CD_SETUP.md -COVERAGE_VERIFICATION.md -STITCH_UI_DESIGN_PROMPT.md -QUICK_REFERENCE.md -CI_CD_IMPROVEMENTS_SUMMARY.md +# Env files +infra/.env +*.pem -# DESIGN FILES -design/ \ No newline at end of file +# Expo / React Native +packages/mobile/.expo/ +packages/mobile/node_modules/ +packages/mobile/dist/ + +# Go workspace sum +go.work.sum + +# RSA +dev-private.pem +dev-public.pem \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f85d260 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run CodeLink Extension", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}/packages/vscode-extension"], + "outFiles": ["${workspaceFolder}/packages/vscode-extension/dist/**/*.js"], + "preLaunchTask": "build-extension" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..b1a2e7f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,21 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build-extension", + "type": "shell", + "command": "npm run build", + "options": { + "cwd": "${workspaceFolder}/packages/vscode-extension" + }, + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$tsc" + } + ] +} diff --git a/README.md b/README.md index c3b4aa6..34cb699 100644 --- a/README.md +++ b/README.md @@ -1,704 +1,221 @@ -# CodeLink - -CodeLink is a development tool that enables AI-assisted code editing through a mobile interface, without requiring cloud IDEs or repository synchronization. All changes require human approval before being applied. - -## Architecture Overview - -CodeLink consists of three main components: +# πŸ”— CodeLink + +CodeLink is a premium developer tool that brings AI-assisted coding to your mobile device. Review diffs, track unsaved changes, compose AI prompts, and securely inject instructions directly into your local AI code editor (Continue, Kiro, Cursor, or Antigravity) β€” all from your phone, without cloud dependencies or synchronizing repositories. + +--- + +## πŸ—οΈ Architecture Overview + +CodeLink is built with a highly secure, modular, and performant monorepo architecture: + +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ VS Code Extension β”‚ (TypeScript Host) + β”‚ - FileWatcher & Git Diffs β”‚ + β”‚ - Editor Adapter Registry β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ WebSockets (JWT Auth) + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Docker Compose Backend β”‚ + β”‚ β”‚ + β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ + β”‚ β”‚ Auth Service │◀────▢│ Relay Service β”‚ β”‚ + β”‚ β”‚ (Go REST) β”‚ β”‚ (Go WebSocket) β”‚ β”‚ + β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ + β”‚ β–Ό β–Ό β”‚ + β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ + β”‚ β”‚ PostgreSQL β”‚ β”‚ Redis Cache β”‚ β”‚ + β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ WebSockets (JWT Auth) + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Mobile Client β”‚ (React Native / Expo) + β”‚ - Unified Mobile Diff View β”‚ + β”‚ - Prompt Composer Console β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` ### 1. VS Code Extension (`packages/vscode-extension`) -A Visual Studio Code extension that integrates with your local development environment. It communicates with the relay server to receive code change requests from the mobile client. - -**Git Integration Components**: - -- **File Watcher**: Monitors active editor changes with 1000ms debouncing -- **Git Integration Module**: Fetches HEAD versions from local Git repository using simple-git -- **Diff Generator**: Compares HEAD vs current file state and generates FileContextPayload -- **WebSocket Client**: Transmits SYNC_FULL_CONTEXT messages to relay server - -**Editor Adapter System**: - -- **Editor Registry**: Manages and detects available AI code editors (Continue, Kiro, Cursor, Antigravity) -- **Capability-Driven Architecture**: Adapts behavior based on what each editor supports -- **Prompt Injection**: Sends prompts from mobile to AI editor chat panels using public VS Code commands -- **Safe Integration**: Uses only public VS Code APIs, no UI scraping or private APIs - -### 2. Relay Server (`packages/relay-server`) - -A WebSocket relay server built with Socket.IO that facilitates real-time communication between the VS Code extension and the mobile client. It runs locally on your machine and handles message routing. - -**Message Routing**: Routes SYNC_FULL_CONTEXT messages from VS Code extension to all connected mobile clients. +A TypeScript host extension that integrates into your local workspace. -### 3. Mobile Client (`packages/mobile-client`) +- **Git Integration**: Uses a high-performance `FileWatcher` (debounced) and `SnapshotEngine` to compute differences between current file states and Git `HEAD`. +- **Patch Engine**: Compiles patches using `PatchEncoder` and transmits them as `SYNC_FULL_CONTEXT` message envelopes. +- **Editor Adapter Registry**: Detects installed AI editors (Continue, Kiro, Cursor, Antigravity) and safely injects prompts using public VS Code command APIs, with no fragile UI scraping. +- **Authentication**: Generates RSA keys locally via `KeyManager` to securely pair with mobile devices. -A Progressive Web App (PWA) built with React that provides a mobile interface for reviewing and approving code changes. It connects to the relay server via WebSocket. +### 2. Auth Service (`services/auth`) -**Diff Viewer**: Renders unified diffs using react-diff-viewer-continued with mobile-optimized styling, dirty state indicators, and timestamp display. +A microservice written in Go that acts as the source of truth for identity and trust. -### 4. Protocol Package (`packages/protocol`) +- **Laptop Management**: Stores registered laptop identities and public keys in PostgreSQL. +- **Session Control**: Manages user authentication, pairing requests, and signs secure JWTs using its private key. +- **Revocations**: Coordinates session cancellations and tracks session invalidations in Redis. -A shared TypeScript package that defines the message types and interfaces used for communication between all components. +### 3. Relay Service (`services/relay`) -**Message Types**: Includes SYNC_FULL_CONTEXT message type and FileContextPayload interface for Git integration. +A high-throughput WebSocket routing hub built in Go. -## Architecture Diagram +- **Secure Routing**: Validates JWT signatures using the Auth Service's public key. +- **Real-Time Delivery**: Acts as a stateful WebSocket tunnel routing prompt commands and code diffs between connected extensions and paired mobile devices. +- **PubSub Integration**: Utilizes Redis for cross-node message distribution and connection tracking. -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ VS Code Extension β”‚ -β”‚ β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ File Watcher │───▢│ Git Integration │───▢│ Diff Generator β”‚ β”‚ -β”‚ β”‚ β”‚ β”‚ Module β”‚ β”‚ β”‚ β”‚ -β”‚ β”‚ - Debounce β”‚ β”‚ - Fetch HEAD β”‚ β”‚ - Compare β”‚ β”‚ -β”‚ β”‚ - Track file β”‚ β”‚ - Check tracked β”‚ β”‚ - Build payload β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β”‚ β”‚ β”‚ -β”‚ β–Ό β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ WebSocket Client β”‚ β”‚ -β”‚ β”‚ - Send message β”‚ β”‚ -β”‚ β”‚ - Queue/retry β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”‚ SYNC_FULL_CONTEXT - β”‚ message - β–Ό -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Relay Server β”‚ -β”‚ β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ Message Router β”‚ β”‚ -β”‚ β”‚ - Receive from VS Code β”‚ β”‚ -β”‚ β”‚ - Broadcast to mobile β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”‚ Forward to all - β”‚ mobile clients - β–Ό -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Mobile Client (PWA) β”‚ -β”‚ β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ WebSocket Client │───▢│ Diff Viewer β”‚ β”‚ -β”‚ β”‚ - Receive msg β”‚ β”‚ - Render unified diff β”‚ β”‚ -β”‚ β”‚ - Parse payload β”‚ β”‚ - Show dirty indicator β”‚ β”‚ -β”‚ β”‚ - Handle errors β”‚ β”‚ - Display timestamp β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ - Mobile-optimized layout β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - -External Dependencies: -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Git Repository β”‚ β”‚ File System β”‚ -β”‚ - HEAD content β”‚ β”‚ - Current files β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β–² β–² - β”‚ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - Used by VS Code Extension -``` - -**Data Flow**: - -1. User edits file in VS Code -2. File Watcher detects change (after 1000ms debounce) -3. Git Integration Module fetches HEAD version from Git repository -4. Git Integration Module reads current file from disk -5. Diff Generator compares HEAD vs current and creates FileContextPayload -6. WebSocket Client sends SYNC_FULL_CONTEXT message to Relay Server -7. Relay Server broadcasts message to all connected Mobile Clients -8. Mobile Client receives message and renders diff in Diff Viewer - -## Core Principles - -- **No Cloud IDE**: All code editing happens in your local VS Code instance -- **No Repository Sync**: No automatic syncing or pushing to remote repositories -- **Human Approval Required**: All code changes must be explicitly approved by the developer -- **Unified Diff as Primary UI**: Code changes are presented as unified diffs for easy review +### 4. Mobile Client (`packages/mobile`) -## Features +A modern, native-feeling Expo/React Native application for iOS, Android, and web. -### Editor Adapter System +- **QR Pairing**: Scans QR codes generated by the VS Code extension to establish secure pairing. +- **Diff Viewer**: Displays unified code diffs using a mobile-optimized layout with unsaved "dirty" state indicators. +- **Prompt Composer**: Features a sleek console for drafting prompt instructions and sending them directly to the active VS Code AI editor. -CodeLink integrates with multiple AI code editors through a capability-driven adapter system. This allows you to send prompts from your mobile device to any supported AI editor running in VS Code. +### 5. Protocol Package (`packages/protocol`) -#### Supported Editors +A shared TypeScript library containing the core message type definitions, JSON codecs, type guards, and envelope encoders/decoders (`buildEnvelope`, `parseEnvelope`). -| Editor | Prompt Injection | Chat History | Token Streaming | Diff Artifacts | Sync Level | -| --------------- | ---------------- | ------------ | --------------- | -------------- | ------------ | -| **Continue** | βœ… | βœ… | βœ… | βœ… | Full | -| **Kiro** | βœ… | βœ… | βœ… | βœ… | Partial | -| **Cursor** | βœ… | ❌ | ❌ | ❌ | Control-Only | -| **Antigravity** | βœ… | ❌ | ❌ | ❌ | Control-Only | +--- -#### How It Works +## πŸ“‚ Project Structure -The Editor Adapter System automatically detects which AI editors are installed in your VS Code environment and selects the best available option based on capabilities: - -``` -Mobile Client (send prompt) - ↓ -Relay Server (route message) - ↓ -VS Code Extension (receive INJECT_PROMPT) - ↓ -Editor Registry (select best adapter) - ↓ -Editor Adapter (inject via VS Code command) - ↓ -AI Editor (display prompt in chat panel) ``` - -#### Key Capabilities - -- **Automatic Detection**: Discovers installed editors by querying VS Code commands -- **Capability-Driven**: Adapts behavior based on what each editor supports -- **Safe Integration**: Uses only public VS Code APIs (`vscode.commands.executeCommand`) -- **Graceful Degradation**: Falls back to control-only mode for closed-source editors -- **Error Handling**: Provides clear error messages when operations fail -- **No UI Scraping**: Never uses webview DOM access, keystroke replay, or private APIs - -#### Sync Levels Explained - -- **Full Sync**: Complete access to chat history, token streaming, and diff artifacts (Continue only) -- **Partial Sync**: Access to own internal state but limited external API (Kiro) -- **Control-Only**: Can inject prompts but cannot read chat history or stream tokens (Cursor, Antigravity) - -#### Using Prompt Injection from Mobile - -1. **Ensure an AI editor is installed**: Install Continue, Kiro, Cursor, or Antigravity in VS Code -2. **Connect mobile client**: Open the mobile client and verify connection status -3. **Send a prompt**: Type your prompt in the mobile interface and send -4. **View in editor**: The prompt appears in your AI editor's chat panel as if you typed it - -#### Message Protocol - -Prompt injection uses the `INJECT_PROMPT` message type: - -```typescript -interface InjectPromptMessage { - type: 'INJECT_PROMPT'; - payload: { - prompt: string; - }; - timestamp: number; -} - -interface InjectPromptResponse { - type: 'INJECT_PROMPT_RESPONSE'; - payload: { - success: boolean; - error?: string; - editorUsed?: string; - }; - timestamp: number; -} +codelink/ +β”œβ”€β”€ packages/ +β”‚ β”œβ”€β”€ protocol/ # Shared TypeScript contracts and encoders (Library) +β”‚ β”œβ”€β”€ vscode-extension/ # VS Code Host Extension (TypeScript) +β”‚ └── mobile/ # Expo React Native App (iOS / Android / Web) +β”œβ”€β”€ services/ +β”‚ β”œβ”€β”€ auth/ # Identity and Token Issuance Service (Go) +β”‚ └── relay/ # WebSocket Message Routing Hub (Go) +β”œβ”€β”€ infra/ +β”‚ β”œβ”€β”€ docker-compose.yml # Backend dependencies and service orchestration +β”‚ └── .env.example # Backend environmental variables template +β”œβ”€β”€ scripts/ +β”‚ β”œβ”€β”€ setup-git-hooks.sh # Helper script to install pre-commit git hooks +β”‚ └── README.md # Comprehensive reference of development scripts +β”œβ”€β”€ tsconfig.base.json # Base TypeScript compiler directives +β”œβ”€β”€ tsconfig.json # Solution-style TypeScript compiler targets +└── package.json # Root workspace configuration ``` -**Message Flow**: +--- -1. Mobile client sends `INJECT_PROMPT` message to relay server -2. Relay server routes message to VS Code extension -3. Extension queries Editor Registry for best available adapter -4. Adapter executes VS Code command to inject prompt into editor -5. Extension sends `INJECT_PROMPT_RESPONSE` back to mobile client +## πŸš€ Local Development Setup -#### Architecture Principles +Get the full development environment running locally in three simple steps: -The Editor Adapter System follows these design principles: +### Step 1: Start Backend Infrastructure -- **Editor-Agnostic Core**: No editor-specific logic outside adapter implementations -- **Capability Honesty**: Adapters only claim capabilities they can actually provide -- **Fail-Safe Operations**: All operations return error results instead of throwing exceptions -- **Extensible Design**: New editors can be added without modifying core modules -- **Transparent Limitations**: Clearly documents what is and isn't possible per editor +Spin up Postgres, Redis, the Auth service, and the Relay service in Docker: -#### Troubleshooting - -**Prompt injection not working**: - -- Verify an AI editor is installed in VS Code -- Check VS Code Output panel for CodeLink extension logs -- Ensure the editor's extension is activated (open its chat panel once) -- Review the error message in the mobile client response - -**No editor detected**: - -- Install at least one supported AI editor (Continue, Kiro, Cursor, or Antigravity) -- Restart VS Code after installing an editor -- Check that the editor extension is enabled in VS Code -- Run "Developer: Show Running Extensions" to verify the editor is active - -**Wrong editor selected**: - -- The system automatically selects the editor with the highest sync level -- Preference order: Continue (full) > Kiro (partial) > Cursor/Antigravity (control-only) -- To force a specific editor, disable other AI editor extensions - -**Command execution fails**: - -- Ensure the AI editor is fully initialized (may take a few seconds after VS Code starts) -- Check that the editor's chat panel can be opened manually -- Review VS Code extension logs for detailed error messages -- Try reloading VS Code window (Ctrl+R / Cmd+R in Extension Development Host) - -### Git Integration & File Diffing - -CodeLink provides real-time unified diff viewing on mobile devices, allowing you to monitor your code changes as you work. The system automatically detects file edits in VS Code, compares them against the Git HEAD version, and displays the differences on your mobile device. - -#### How It Works - -The Git Integration feature follows a pipeline architecture: - -``` -File Edit in VS Code - ↓ -File Watcher (1000ms debounce) - ↓ -Git Integration Module (fetch HEAD version) - ↓ -Diff Generator (compare HEAD vs current) - ↓ -WebSocket Client (send to relay server) - ↓ -Relay Server (route to mobile clients) - ↓ -Mobile Client (render unified diff) -``` - -#### Key Capabilities - -- **Automatic Change Detection**: Monitors active file changes in VS Code with intelligent debouncing -- **Git Integration**: Fetches HEAD versions from your local Git repository using simple-git -- **Real-time Diff Transmission**: Sends unified diffs via WebSocket to connected mobile devices -- **Mobile-Optimized Display**: Renders diffs using react-diff-viewer-continued in unified view mode -- **Dirty State Tracking**: Shows visual indicators for files with unsaved changes -- **Untracked File Support**: Handles files not yet committed to Git gracefully -- **Performance Optimized**: End-to-end latency under 2 seconds for typical files - -#### Using the Diff Viewer on Mobile - -1. **Connect to the relay server**: Open the mobile client at http://localhost:3000 (or your configured URL) -2. **Verify connection**: Check that the status shows "Connected" in green -3. **Edit files in VS Code**: Open any file in your workspace and make changes -4. **View diffs automatically**: After you stop typing (1 second delay), the diff appears on your mobile device -5. **Review changes**: The diff viewer shows: - - File name and path - - Orange dot indicator for unsaved changes - - Timestamp of when the diff was generated - - Line-by-line comparison with additions (green) and deletions (red) - - Unified view optimized for mobile screens - -#### Diff Viewer Features - -- **Unified View Mode**: Single-column diff display optimized for mobile screens -- **Syntax Highlighting**: Code is displayed with appropriate syntax coloring -- **Dark Theme**: Matches VS Code's dark theme for consistency -- **Dirty Indicator**: Orange dot (●) appears when file has unsaved changes -- **Timestamp Display**: Shows when the diff was last generated -- **New File Support**: Files not in Git are shown as all additions -- **No Changes Message**: Clear indication when file matches HEAD version - -#### WebSocket Message Protocol - -The Git Integration feature uses the `SYNC_FULL_CONTEXT` message type to transmit diff data: - -```typescript -interface SyncFullContextMessage { - type: 'SYNC_FULL_CONTEXT'; - payload: FileContextPayload; - timestamp: number; -} - -interface FileContextPayload { - fileName: string; // Workspace-relative path (e.g., "src/index.ts") - originalFile: string; // Content from Git HEAD (empty if untracked) - modifiedFile: string; // Current file content from disk - isDirty: boolean; // True if file has unsaved changes - timestamp: number; // Unix timestamp in milliseconds -} +```bash +# Build and start backing services and microservices +docker compose -f infra/docker-compose.yml up --build ``` -**Message Flow**: - -1. VS Code extension creates `SYNC_FULL_CONTEXT` message with `FileContextPayload` -2. Message is sent to relay server via WebSocket -3. Relay server broadcasts message to all connected mobile clients -4. Mobile client parses payload and renders diff using react-diff-viewer-continued - -#### Performance Characteristics - -The Git Integration feature is designed for responsive real-time feedback: - -- **Debounce Delay**: 1000ms after last keystroke before diff generation -- **Git Operations**: Typically complete in under 500ms for standard files -- **Diff Generation**: Completes in under 200ms for files under 10,000 lines -- **WebSocket Latency**: Under 300ms on typical local networks -- **End-to-End Latency**: Total time from last keystroke to mobile display is under 2 seconds - -**Performance Tips**: +_Backends will be available on standard local ports: Auth REST on `8081`, Relay WebSocket on `8082`._ -- Large files (>10,000 lines) may take longer to process -- Binary files are automatically skipped -- Network latency affects WebSocket transmission time -- Multiple rapid edits are debounced to avoid excessive processing +### Step 2: Build Protocol & Extension -#### Troubleshooting - -**Diffs not appearing on mobile**: - -- Verify the relay server is running and accessible -- Check that the mobile client shows "Connected" status -- Ensure the file is within your VS Code workspace -- Check VS Code Output panel for CodeLink extension logs -- Verify the file is a text file (binary files are skipped) - -**Empty diffs for tracked files**: - -- Ensure the file is committed to Git (check `git status`) -- Verify Git repository is initialized in your workspace -- Check that the file path is correct and relative to workspace root -- Review VS Code extension logs for Git operation errors - -**Performance issues**: - -- Large files (>10,000 lines) may experience slower processing -- Check network latency between VS Code and relay server -- Verify Git operations are not timing out (check logs) -- Consider closing unused files to reduce monitoring overhead - -**Git repository not found**: - -- Ensure your workspace is within a Git repository -- Run `git rev-parse --show-toplevel` to verify Git is initialized -- Check that the VS Code workspace folder is correctly configured -- Review extension logs for Git initialization errors - -**WebSocket connection issues**: - -- Verify relay server is running on the expected port (default: 8080) -- Check firewall settings allow WebSocket connections -- Ensure no other service is using the relay server port -- Review browser console for WebSocket connection errors - -**Unsaved changes not reflected**: - -- The diff shows disk content, not unsaved editor content -- Save the file (Ctrl+S / Cmd+S) to see unsaved changes in the diff -- The orange dot indicator shows when changes are unsaved -- isDirty flag tracks unsaved state separately from diff content - -**Untracked files showing as all additions**: - -- This is expected behavior for files not committed to Git -- The originalFile will be empty for untracked files -- Commit the file to Git to see proper diffs -- Use `git add ` and `git commit` to track the file - -## Setup Instructions - -### Prerequisites - -- Node.js 20.x or higher -- npm 9.x or higher - -### Installation - -1. Clone the repository and install dependencies: +In a new terminal window, build the shared package and the extension: ```bash +# Install workspace dependencies npm install -``` -2. Build all packages: - -```bash +# Compile shared protocol and typescript packages npm run build ``` -## Development Instructions +### Step 3: Run Clients -### Running All Components +#### Launch VS Code Extension Development Host -Start all components in development mode: +1. Open the repository root folder in VS Code. +2. Navigate to `packages/vscode-extension/src/extension.ts`. +3. Press `F5` (or go to Run and Debug -> click "Run Extension"). +4. A new "Extension Development Host" VS Code window will launch with CodeLink active. -```bash -npm run dev -``` - -### Running Components Individually +#### Launch Mobile Client (Expo) -**Protocol Package** (build and watch): +In another terminal, start the Expo development server: ```bash -cd packages/protocol -npm run dev -``` - -**VS Code Extension** (build and watch): - -```bash -cd packages/vscode-extension -npm run dev -``` - -Then press F5 in VS Code to launch the Extension Development Host. - -**Relay Server** (start server): - -```bash -cd packages/relay-server -npm run build +cd packages/mobile npm start ``` -The relay server will listen on port 8080 by default. You can change this by setting the `PORT` environment variable: - -```bash -PORT=3001 npm start -``` +_Press `a` to run in an Android Emulator, `i` to run in iOS Simulator, or `w` to run in your local web browser._ -**Note**: The `npm run dev` command only compiles TypeScript in watch mode - it does not start the server. To run the server during development, use two terminals: +--- -Terminal 1 (compile in watch mode): +## πŸ§ͺ Testing & Validation -```bash -cd packages/relay-server -npm run dev -``` +CodeLink incorporates standard NPM workspace commands and native Go unit tests: -Terminal 2 (run the server): +### TypeScript Workspace Quality Tools ```bash -cd packages/relay-server -npm start -``` - -**Mobile Client** (development server): - -```bash -cd packages/mobile-client -npm run dev -``` - -The mobile client will be available at http://localhost:3000 - -## Testing the Complete Flow - -To verify the entire system is working: - -1. **Start the relay server**: - - ```bash - cd packages/relay-server - npm run build - npm start - ``` - - You should see: `CodeLink Relay Server listening on port 8080` - -2. **Start the mobile client**: - - ```bash - cd packages/mobile-client - npm run dev - ``` - - Open http://localhost:3000 in your browser - -3. **Verify the connection**: - - The mobile client should show status: "Connected" (in green) - - Check the browser console - you should see: - - "Connected to relay server" - - "Sent ping: {id, timestamp, type, source}" - - "Received message: {id, timestamp, type, originalId}" - - Check the relay server terminal - you should see: - - "Client connected: [socket-id]" - - "Received message: {type: 'ping', ...}" - - "Sent pong: {type: 'pong', ...}" - - The mobile client UI should display the "Last Pong Received" section with message details - -4. **Test the VS Code extension** (optional): - - Open the VS Code workspace - - Press F5 to launch Extension Development Host - - Open Command Palette (Ctrl+Shift+P / Cmd+Shift+P) - - Run "CodeLink: Hello World" - - You should see a notification with a ping message ID - -## Code Quality Scripts - -### CI/CD Pipeline - -CodeLink uses GitHub Actions for continuous integration. The pipeline runs on every commit and includes: - -- **Linting**: ESLint checks for code quality -- **Type Checking**: TypeScript compilation for all packages -- **Formatting**: Prettier verification -- **Testing**: Comprehensive test suites with 80% coverage requirement - -See [CI/CD Setup Documentation](.github/CI_CD_SETUP.md) for detailed information. - -### Pre-Commit Hooks - -Install Git hooks to run checks before each commit: - -```bash -./scripts/setup-git-hooks.sh -``` - -This will automatically run linting, type checking, and formatting checks before allowing commits. - -### Linting - -Check code for linting errors: +# Compile and build typescript packages +npm run build -```bash +# Run TypeScript linter npm run lint -``` -Auto-fix linting issues: - -```bash +# Auto-fix linter issues npm run lint:fix -``` - -### Type Checking - -Check TypeScript compilation for all packages: - -```bash -npm run typecheck -``` - -Check individual packages: - -```bash -npm run typecheck:protocol # Protocol package -npm run typecheck:relay # Relay server -npm run typecheck:vscode # VS Code extension -npm run typecheck:mobile # Mobile client -``` - -### Formatting -Format all code files: +# Check formatting via Prettier +npm run format:check -```bash +# Format files matching styling policies npm run format -``` -Check if code is properly formatted: +# Run TS typechecks across workspaces +npm run typecheck -```bash -npm run format:check +# Run Vitest unit tests +npm run test ``` -### Pre-Commit Check - -Run all checks (lint + typecheck + format): +### Go Service Quality Tools ```bash -npm run precommit -``` - -### Testing +# Run unit tests across auth and relay microservices +npm run test:go -Run all tests: - -```bash -npm test +# Run verbose Go tests +npm run test:go:verbose ``` -Run tests in watch mode: - -```bash -npm run test:watch -``` +### Git Pre-Commit Hook Integration -Run tests with coverage: +Prevent invalid changes from entering git by installing the pre-commit sanity checks hook: ```bash -npm run test:coverage +# Make the helper script executable and execute it +chmod +x scripts/setup-git-hooks.sh +./scripts/setup-git-hooks.sh ``` -## Development Workflow - -### Starting a New Feature - -1. Create a feature branch: - - ```bash - git checkout -b feature/your-feature-name - ``` - -2. Install pre-commit hooks (first time only): - - ```bash - ./scripts/setup-git-hooks.sh - ``` +_This installs a hook that automatically executes `npm run precommit` before every commit, keeping your repository history clean._ -3. Make your changes and commit: +--- - ```bash - git add . - git commit -m "feat: your feature description" - ``` +## 🚒 Production Deployment - The pre-commit hook will automatically run linting, type checking, and formatting checks. +For production deployments, the modular microservices can be scaled independently: -4. Push your changes: +- **Database & Cache**: Deploy highly available managed PostgreSQL and Redis clusters. +- **Go Services**: Run inside Kubernetes or container platforms behind a secure load balancer (ensure WebSocket-sticky sessions on Relay Ingress). +- **Secrets Management**: Securely provide the `AUTH_HMAC_SECRET`, JWT Private Key PEM, and database connection strings using a dedicated secrets manager. +- **Mobile Assets**: Build native Android/iOS bundles via EAS CLI (`expo build`), or compile PWA static assets to host on CDN networks. - ```bash - git push origin feature/your-feature-name - ``` - -5. Create a pull request on GitHub - - CI/CD pipeline will run automatically - - All checks must pass before merging - - At least one approval is required - -### Before Committing - -Always run these checks locally: - -```bash -# Run all pre-commit checks -npm run precommit - -# Or run individually -npm run lint -npm run typecheck -npm run format:check -npm test -``` - -### Bypassing Pre-Commit Hooks - -In rare cases where you need to bypass the pre-commit hook (not recommended): - -```bash -git commit --no-verify -m "your message" -``` - -## Project Structure - -``` -codelink/ -β”œβ”€β”€ packages/ -β”‚ β”œβ”€β”€ protocol/ # Shared TypeScript types and interfaces -β”‚ β”œβ”€β”€ vscode-extension/ # VS Code extension -β”‚ β”œβ”€β”€ relay-server/ # WebSocket relay server -β”‚ └── mobile-client/ # React PWA mobile client -β”œβ”€β”€ tests/ # Integration and property-based tests -β”œβ”€β”€ package.json # Root workspace configuration -β”œβ”€β”€ tsconfig.base.json # Shared TypeScript configuration -└── README.md # This file -``` +--- -## License +## πŸ“„ License -MIT +CodeLink is open-source software licensed under the [MIT License](LICENSE). diff --git a/app.json b/app.json new file mode 100644 index 0000000..059673a --- /dev/null +++ b/app.json @@ -0,0 +1,12 @@ +{ + "expo": { + "extra": { + "eas": { + "projectId": "fbcaaefe-5e2b-40fd-8e50-3c4f846a52fd" + } + }, + "android": { + "package": "com.gagandevv.codelink" + } + } +} diff --git a/dist/packages/protocol/src/index.test.d.ts b/dist/packages/protocol/src/index.test.d.ts new file mode 100644 index 0000000..121d59b --- /dev/null +++ b/dist/packages/protocol/src/index.test.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=index.test.d.ts.map \ No newline at end of file diff --git a/dist/packages/protocol/src/index.test.d.ts.map b/dist/packages/protocol/src/index.test.d.ts.map new file mode 100644 index 0000000..1711928 --- /dev/null +++ b/dist/packages/protocol/src/index.test.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../packages/protocol/src/index.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/packages/protocol/src/index.test.js b/dist/packages/protocol/src/index.test.js new file mode 100644 index 0000000..6e03813 --- /dev/null +++ b/dist/packages/protocol/src/index.test.js @@ -0,0 +1,129 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const vitest_1 = require("vitest"); +const index_1 = require("./index"); +// ── isMessageEnvelope ───────────────────────────────────────────────────────── +(0, vitest_1.describe)('isMessageEnvelope', () => { + (0, vitest_1.it)('accepts a valid envelope', () => { + (0, vitest_1.expect)((0, index_1.isMessageEnvelope)({ + v: index_1.PROTOCOL_VERSION, + id: 'abc-123', + ts: Date.now(), + type: 'FILE_SNAPSHOT', + payload: {}, + })).toBe(true); + }); + (0, vitest_1.it)('rejects wrong protocol version', () => { + (0, vitest_1.expect)((0, index_1.isMessageEnvelope)({ + v: 99, id: 'x', ts: 0, type: 'PING', payload: {}, + })).toBe(false); + }); + (0, vitest_1.it)('rejects missing payload', () => { + (0, vitest_1.expect)((0, index_1.isMessageEnvelope)({ + v: index_1.PROTOCOL_VERSION, id: 'x', ts: 0, type: 'PING', + })).toBe(false); + }); + (0, vitest_1.it)('rejects null', () => (0, vitest_1.expect)((0, index_1.isMessageEnvelope)(null)).toBe(false)); + (0, vitest_1.it)('rejects string', () => (0, vitest_1.expect)((0, index_1.isMessageEnvelope)('hello')).toBe(false)); +}); +// ── isFileSnapshotPayload ───────────────────────────────────────────────────── +(0, vitest_1.describe)('isFileSnapshotPayload', () => { + const valid = { + fileName: 'src/main.ts', + content: 'console.log("hello")', + encoding: 'utf8', + seq: 1, + isDirty: false, + gitHead: true, + timestamp: Date.now(), + }; + (0, vitest_1.it)('accepts a valid payload', () => (0, vitest_1.expect)((0, index_1.isFileSnapshotPayload)(valid)).toBe(true)); + (0, vitest_1.it)('accepts gzip+base64 encoding', () => (0, vitest_1.expect)((0, index_1.isFileSnapshotPayload)({ ...valid, encoding: 'gzip+base64' })).toBe(true)); + (0, vitest_1.it)('rejects invalid encoding', () => (0, vitest_1.expect)((0, index_1.isFileSnapshotPayload)({ ...valid, encoding: 'ascii' })).toBe(false)); + (0, vitest_1.it)('rejects missing fileName', () => (0, vitest_1.expect)((0, index_1.isFileSnapshotPayload)({ ...valid, fileName: undefined })).toBe(false)); + (0, vitest_1.it)('rejects non-number seq', () => (0, vitest_1.expect)((0, index_1.isFileSnapshotPayload)({ ...valid, seq: '1' })).toBe(false)); +}); +// ── isFilePatchPayload ──────────────────────────────────────────────────────── +(0, vitest_1.describe)('isFilePatchPayload', () => { + const valid = { + fileName: 'src/main.ts', + patches: '@@ -1,4 +1,5 @@\n hello\n+world\n', + fromSeq: 1, + toSeq: 2, + isDirty: true, + timestamp: Date.now(), + }; + (0, vitest_1.it)('accepts a valid payload', () => (0, vitest_1.expect)((0, index_1.isFilePatchPayload)(valid)).toBe(true)); + (0, vitest_1.it)('rejects missing patches', () => (0, vitest_1.expect)((0, index_1.isFilePatchPayload)({ ...valid, patches: undefined })).toBe(false)); + (0, vitest_1.it)('rejects string fromSeq', () => (0, vitest_1.expect)((0, index_1.isFilePatchPayload)({ ...valid, fromSeq: '1' })).toBe(false)); + (0, vitest_1.it)('rejects null', () => (0, vitest_1.expect)((0, index_1.isFilePatchPayload)(null)).toBe(false)); +}); +// ── isInjectPromptPayload ───────────────────────────────────────────────────── +(0, vitest_1.describe)('isInjectPromptPayload', () => { + (0, vitest_1.it)('accepts a valid payload', () => (0, vitest_1.expect)((0, index_1.isInjectPromptPayload)({ prompt: 'Refactor this' })).toBe(true)); + (0, vitest_1.it)('accepts optional fields', () => (0, vitest_1.expect)((0, index_1.isInjectPromptPayload)({ prompt: 'x', targetFile: 'src/a.ts' })).toBe(true)); + (0, vitest_1.it)('rejects empty prompt', () => (0, vitest_1.expect)((0, index_1.isInjectPromptPayload)({ prompt: '' })).toBe(false)); + (0, vitest_1.it)('rejects missing prompt', () => (0, vitest_1.expect)((0, index_1.isInjectPromptPayload)({})).toBe(false)); +}); +// ── isSnapshotRequestPayload ───────────────────────────────────────────────────── +(0, vitest_1.describe)('isSnapshotRequestPayload', () => { + (0, vitest_1.it)('accepts valid payload', () => (0, vitest_1.expect)((0, index_1.isSnapshotRequestPayload)({ fileName: 'a.ts', reason: 'gap' })).toBe(true)); + (0, vitest_1.it)('accepts all reason values', () => { + (0, vitest_1.expect)((0, index_1.isSnapshotRequestPayload)({ fileName: 'a.ts', reason: 'corruption' })).toBe(true); + (0, vitest_1.expect)((0, index_1.isSnapshotRequestPayload)({ fileName: 'a.ts', reason: 'reconnect' })).toBe(true); + }); + (0, vitest_1.it)('rejects invalid reason', () => (0, vitest_1.expect)((0, index_1.isSnapshotRequestPayload)({ fileName: 'a.ts', reason: 'unknown' })).toBe(false)); + (0, vitest_1.it)('rejects empty fileName', () => (0, vitest_1.expect)((0, index_1.isSnapshotRequestPayload)({ fileName: '', reason: 'gap' })).toBe(false)); + (0, vitest_1.it)('rejects null', () => (0, vitest_1.expect)((0, index_1.isSnapshotRequestPayload)(null)).toBe(false)); +}); +// ── buildEnvelope ───────────────────────────────────────────────────────────── +(0, vitest_1.describe)('buildEnvelope', () => { + (0, vitest_1.it)('produces a valid envelope', () => { + const env = (0, index_1.buildEnvelope)('PING', { source: 'host' }); + (0, vitest_1.expect)(env.v).toBe(index_1.PROTOCOL_VERSION); + (0, vitest_1.expect)(env.type).toBe('PING'); + (0, vitest_1.expect)(env.payload).toEqual({ source: 'host' }); + (0, vitest_1.expect)(typeof env.id).toBe('string'); + (0, vitest_1.expect)(typeof env.ts).toBe('number'); + (0, vitest_1.expect)(typeof env.seq).toBe('number'); + }); + (0, vitest_1.it)('increments seq on each call', () => { + const a = (0, index_1.buildEnvelope)('PING', { source: 'host' }); + const b = (0, index_1.buildEnvelope)('PING', { source: 'client' }); + (0, vitest_1.expect)(b.seq).toBeGreaterThan(a.seq); + }); + (0, vitest_1.it)('includes ack when provided', () => { + const env = (0, index_1.buildEnvelope)('PONG', { originalId: 'abc' }, { ack: 'abc' }); + (0, vitest_1.expect)(env.ack).toBe('abc'); + }); + (0, vitest_1.it)('omits ack when not provided', () => { + const env = (0, index_1.buildEnvelope)('PING', { source: 'host' }); + (0, vitest_1.expect)(env.ack).toBeUndefined(); + }); +}); +// ── parseEnvelope ───────────────────────────────────────────────────────────── +(0, vitest_1.describe)('parseEnvelope', () => { + (0, vitest_1.it)('parses a valid JSON envelope', () => { + const env = (0, index_1.buildEnvelope)('EDITOR_FOCUS', { fileName: 'a.ts', cursorLine: 5, cursorCol: 3 }); + const json = JSON.stringify(env); + const parsed = (0, index_1.parseEnvelope)(json); + (0, vitest_1.expect)(parsed).not.toBeNull(); + (0, vitest_1.expect)(parsed?.type).toBe('EDITOR_FOCUS'); + }); + (0, vitest_1.it)('returns null for malformed JSON', () => { + (0, vitest_1.expect)((0, index_1.parseEnvelope)('not json')).toBeNull(); + }); + (0, vitest_1.it)('returns null for valid JSON but invalid envelope', () => { + (0, vitest_1.expect)((0, index_1.parseEnvelope)(JSON.stringify({ foo: 'bar' }))).toBeNull(); + }); +}); +// ── ALL_CAPABILITIES ────────────────────────────────────────────────────────── +(0, vitest_1.describe)('ALL_CAPABILITIES', () => { + (0, vitest_1.it)('includes all expected capabilities', () => { + (0, vitest_1.expect)(index_1.ALL_CAPABILITIES).toContain('diff:snapshot'); + (0, vitest_1.expect)(index_1.ALL_CAPABILITIES).toContain('diff:patch'); + (0, vitest_1.expect)(index_1.ALL_CAPABILITIES).toContain('prompt:inject'); + (0, vitest_1.expect)(index_1.ALL_CAPABILITIES).toContain('session:revoke'); + }); +}); +//# sourceMappingURL=index.test.js.map \ No newline at end of file diff --git a/dist/packages/protocol/src/index.test.js.map b/dist/packages/protocol/src/index.test.js.map new file mode 100644 index 0000000..10ae6a5 --- /dev/null +++ b/dist/packages/protocol/src/index.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../../packages/protocol/src/index.test.ts"],"names":[],"mappings":";;AAAA,mCAA8C;AAC9C,mCAUiB;AAEjB,iFAAiF;AAEjF,IAAA,iBAAQ,EAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAA,WAAE,EAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,IAAA,eAAM,EAAC,IAAA,yBAAiB,EAAC;YACvB,CAAC,EAAQ,wBAAgB;YACzB,EAAE,EAAO,SAAS;YAClB,EAAE,EAAO,IAAI,CAAC,GAAG,EAAE;YACnB,IAAI,EAAK,eAAe;YACxB,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,IAAA,eAAM,EAAC,IAAA,yBAAiB,EAAC;YACvB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;SACjD,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,IAAA,eAAM,EAAC,IAAA,yBAAiB,EAAC;YACvB,CAAC,EAAE,wBAAgB,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM;SAClD,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,cAAc,EAAE,GAAI,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,yBAAiB,EAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACvE,IAAA,WAAE,EAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,yBAAiB,EAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7E,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,IAAA,iBAAQ,EAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,MAAM,KAAK,GAAG;QACZ,QAAQ,EAAE,aAAa;QACvB,OAAO,EAAG,sBAAsB;QAChC,QAAQ,EAAE,MAAe;QACzB,GAAG,EAAO,CAAC;QACX,OAAO,EAAG,KAAK;QACf,OAAO,EAAG,IAAI;QACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IAEF,IAAA,WAAE,EAAC,yBAAyB,EAAS,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5F,IAAA,WAAE,EAAC,8BAA8B,EAAI,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5H,IAAA,WAAE,EAAC,0BAA0B,EAAQ,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACvH,IAAA,WAAE,EAAC,0BAA0B,EAAQ,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACzH,IAAA,WAAE,EAAC,wBAAwB,EAAU,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAChH,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,IAAA,iBAAQ,EAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,MAAM,KAAK,GAAG;QACZ,QAAQ,EAAE,aAAa;QACvB,OAAO,EAAG,mCAAmC;QAC7C,OAAO,EAAG,CAAC;QACX,KAAK,EAAK,CAAC;QACX,OAAO,EAAG,IAAI;QACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IAEF,IAAA,WAAE,EAAC,yBAAyB,EAAG,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,0BAAkB,EAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,IAAA,WAAE,EAAC,yBAAyB,EAAG,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,0BAAkB,EAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/G,IAAA,WAAE,EAAC,wBAAwB,EAAI,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,0BAAkB,EAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACzG,IAAA,WAAE,EAAC,cAAc,EAAc,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,0BAAkB,EAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACrF,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,IAAA,iBAAQ,EAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,IAAA,WAAE,EAAC,yBAAyB,EAAI,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7G,IAAA,WAAE,EAAC,yBAAyB,EAAI,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzH,IAAA,WAAE,EAAC,sBAAsB,EAAO,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACjG,IAAA,WAAE,EAAC,wBAAwB,EAAK,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,6BAAqB,EAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACvF,CAAC,CAAC,CAAC;AAEH,oFAAoF;AACpF,IAAA,iBAAQ,EAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,IAAA,WAAE,EAAC,uBAAuB,EAAO,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,gCAAwB,EAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzH,IAAA,WAAE,EAAC,2BAA2B,EAAG,GAAG,EAAE;QACpC,IAAA,eAAM,EAAC,IAAA,gCAAwB,EAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxF,IAAA,eAAM,EAAC,IAAA,gCAAwB,EAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IACH,IAAA,WAAE,EAAC,wBAAwB,EAAM,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,gCAAwB,EAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9H,IAAA,WAAE,EAAC,wBAAwB,EAAM,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,gCAAwB,EAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACtH,IAAA,WAAE,EAAC,cAAc,EAAgB,GAAG,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,gCAAwB,EAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7F,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,IAAA,iBAAQ,EAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAA,WAAE,EAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,IAAA,qBAAa,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,IAAA,eAAM,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAgB,CAAC,CAAC;QACrC,IAAA,eAAM,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAA,eAAM,EAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,IAAA,eAAM,EAAC,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAA,eAAM,EAAC,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAA,eAAM,EAAC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,IAAA,qBAAa,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,IAAA,qBAAa,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtD,IAAA,eAAM,EAAC,CAAC,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,GAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,GAAG,GAAG,IAAA,qBAAa,EAAC,MAAM,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QACzE,IAAA,eAAM,EAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,GAAG,GAAG,IAAA,qBAAa,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,IAAA,eAAM,EAAC,GAAG,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,IAAA,iBAAQ,EAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAA,WAAE,EAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,GAAG,GAAG,IAAA,qBAAa,EAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7F,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,IAAA,qBAAa,EAAC,IAAI,CAAC,CAAC;QACnC,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,IAAA,eAAM,EAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,IAAA,eAAM,EAAC,IAAA,qBAAa,EAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,IAAA,eAAM,EAAC,IAAA,qBAAa,EAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,IAAA,iBAAQ,EAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,IAAA,WAAE,EAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,IAAA,eAAM,EAAC,wBAAgB,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACpD,IAAA,eAAM,EAAC,wBAAgB,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACjD,IAAA,eAAM,EAAC,wBAAgB,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACpD,IAAA,eAAM,EAAC,wBAAgB,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/vitest.config.d.ts b/dist/vitest.config.d.ts new file mode 100644 index 0000000..58b19a7 --- /dev/null +++ b/dist/vitest.config.d.ts @@ -0,0 +1,7 @@ +/** + * Root Vitest configuration + * Combines Node.js and React Native test environments + */ +declare const _default: UserConfig; +export default _default; +//# sourceMappingURL=vitest.config.d.ts.map \ No newline at end of file diff --git a/dist/vitest.config.d.ts.map b/dist/vitest.config.d.ts.map new file mode 100644 index 0000000..1b73fd7 --- /dev/null +++ b/dist/vitest.config.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAGA;;;GAGG;;AACH,wBAgFG"} \ No newline at end of file diff --git a/dist/vitest.config.js b/dist/vitest.config.js new file mode 100644 index 0000000..e912ffc --- /dev/null +++ b/dist/vitest.config.js @@ -0,0 +1,93 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const config_1 = require("vitest/config"); +const path_1 = __importDefault(require("path")); +/** + * Root Vitest configuration + * Combines Node.js and React Native test environments + */ +exports.default = (0, config_1.defineConfig)({ + test: { + environment: 'node', + include: [ + 'tests/unit/protocol/**/*.test.ts', + 'tests/unit/relay-server/**/*.test.ts', + 'tests/unit/vscode-extension/**/*.test.ts', + 'tests/unit/mobile-client/**/*.test.tsx', + 'tests/unit/mobile-client/**/*.test.ts', + 'tests/integration/**/*.test.ts', + 'tests/property/**/*.test.ts', + 'tests/performance/**/*.test.ts' + ], + exclude: [ + 'tests/unit/mobile-client/components.test.tsx' + ], + setupFiles: ['./tests/setup/test-setup.ts', './tests/setup/react-native-testing-setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'html', 'json', 'lcov'], + include: [ + 'packages/protocol/src/**/*.ts', + 'packages/relay-server/src/**/*.ts', + 'packages/vscode-extension/src/**/*.ts', + 'packages/mobile-client/src/**/*.ts', + 'packages/mobile-client/src/**/*.tsx' + ], + exclude: [ + 'tests/**', + '**/*.test.ts', + '**/*.test.tsx', + '**/dist/**', + '**/node_modules/**', + '**/*.config.js', + '**/*.config.ts' + ], + thresholds: { + lines: 80, + functions: 80, + branches: 80, + statements: 80 + } + }, + globals: true, + testTimeout: 10000, + // Disable source map resolution to prevent loading TypeScript sources + sourcemap: false, + // Use jsdom for React Native component tests + environmentMatchGlobs: [ + ['tests/unit/mobile-client/**', 'jsdom'] + ], + // Transform node_modules that need transpilation + server: { + deps: { + inline: [ + 'react-native', + '@testing-library/react-native', + ], + // Don't externalize @testing-library/react-native so it gets transformed + external: [] + } + }, + // Explicitly exclude node_modules from transformation except those we inline + exclude: [ + ...['node_modules/**'], + 'tests/unit/mobile-client/components.test.tsx' + ] + }, + resolve: { + alias: { + '@codelink/protocol': path_1.default.resolve(__dirname, './packages/protocol/src'), + // Alias react-native to our mock for testing + 'react-native': path_1.default.resolve(__dirname, './tests/setup/react-native-mock.ts'), + // Resolve React from root node_modules (monorepo setup) + 'react': path_1.default.resolve(__dirname, './node_modules/react'), + 'react-dom': path_1.default.resolve(__dirname, './node_modules/react-dom'), + }, + conditions: ['import', 'module', 'browser', 'default'], + extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'] + } +}); +//# sourceMappingURL=vitest.config.js.map \ No newline at end of file diff --git a/dist/vitest.config.js.map b/dist/vitest.config.js.map new file mode 100644 index 0000000..0921065 --- /dev/null +++ b/dist/vitest.config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":";;;;;AAAA,0CAA6C;AAC7C,gDAAwB;AAExB;;;GAGG;AACH,kBAAe,IAAA,qBAAY,EAAC;IAC1B,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE;YACP,kCAAkC;YAClC,sCAAsC;YACtC,0CAA0C;YAC1C,wCAAwC;YACxC,uCAAuC;YACvC,gCAAgC;YAChC,6BAA6B;YAC7B,gCAAgC;SACjC;QACD,OAAO,EAAE;YACP,8CAA8C;SAC/C;QACD,UAAU,EAAE,CAAC,6BAA6B,EAAE,6CAA6C,CAAC;QAC1F,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC1C,OAAO,EAAE;gBACP,+BAA+B;gBAC/B,mCAAmC;gBACnC,uCAAuC;gBACvC,oCAAoC;gBACpC,qCAAqC;aACtC;YACD,OAAO,EAAE;gBACP,UAAU;gBACV,cAAc;gBACd,eAAe;gBACf,YAAY;gBACZ,oBAAoB;gBACpB,gBAAgB;gBAChB,gBAAgB;aACjB;YACD,UAAU,EAAE;gBACV,KAAK,EAAE,EAAE;gBACT,SAAS,EAAE,EAAE;gBACb,QAAQ,EAAE,EAAE;gBACZ,UAAU,EAAE,EAAE;aACf;SACF;QACD,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,KAAK;QAClB,sEAAsE;QACtE,SAAS,EAAE,KAAK;QAChB,6CAA6C;QAC7C,qBAAqB,EAAE;YACrB,CAAC,6BAA6B,EAAE,OAAO,CAAC;SACzC;QACD,iDAAiD;QACjD,MAAM,EAAE;YACN,IAAI,EAAE;gBACJ,MAAM,EAAE;oBACN,cAAc;oBACd,+BAA+B;iBAChC;gBACD,yEAAyE;gBACzE,QAAQ,EAAE,EAAE;aACb;SACF;QACD,6EAA6E;QAC7E,OAAO,EAAE;YACP,GAAG,CAAC,iBAAiB,CAAC;YACtB,8CAA8C;SAC/C;KACF;IACD,OAAO,EAAE;QACP,KAAK,EAAE;YACL,oBAAoB,EAAE,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,yBAAyB,CAAC;YACxE,6CAA6C;YAC7C,cAAc,EAAE,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,oCAAoC,CAAC;YAC7E,wDAAwD;YACxD,OAAO,EAAE,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,sBAAsB,CAAC;YACxD,WAAW,EAAE,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,0BAA0B,CAAC;SACjE;QACD,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC;QACtD,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC;KACpD;CACF,CAAC,CAAC"} \ No newline at end of file diff --git a/eas.json b/eas.json new file mode 100644 index 0000000..d0ff643 --- /dev/null +++ b/eas.json @@ -0,0 +1,21 @@ +{ + "cli": { + "version": ">= 19.0.1", + "appVersionSource": "remote" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "preview": { + "distribution": "internal" + }, + "production": { + "autoIncrement": true + } + }, + "submit": { + "production": {} + } +} diff --git a/go.work b/go.work new file mode 100644 index 0000000..d5f129b --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.26.3 + +use ( + ./services/auth + ./services/relay +) diff --git a/infra/.env.example b/infra/.env.example new file mode 100644 index 0000000..4013983 --- /dev/null +++ b/infra/.env.example @@ -0,0 +1,20 @@ +# ── Auth Service ──────────────────────────────────────────────── +AUTH_PORT=8081 +POSTGRES_URL=postgres://codelink:devpassword@postgres:5432/codelink +REDIS_URL=redis://redis:6379 + +# 32+ random bytes β€” generate with: openssl rand -hex 32 +AUTH_HMAC_SECRET= + +# RSA-2048 private key PEM β€” generate with instructions in README +AUTH_PRIVATE_KEY_PEM= + + +# ── Relay Service ─────────────────────────────────────────────── +RELAY_PORT=8082 +REDIS_URL=redis://redis:6379 + +# RSA-2048 public key PEM β€” matching pair to AUTH_PRIVATE_KEY_PEM +AUTH_PUBLIC_KEY_PEM= + +RELAY_WSS_URL=ws://localhost:8082/ws \ No newline at end of file diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml new file mode 100644 index 0000000..f8752eb --- /dev/null +++ b/infra/docker-compose.yml @@ -0,0 +1,64 @@ +services: + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: codelink + POSTGRES_USER: codelink + POSTGRES_PASSWORD: devpassword + ports: + - "5432:5432" + volumes: + - pg_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U codelink -d codelink"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 10s + + redis: + image: redis:7-alpine + restart: unless-stopped + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + + auth-service: + build: + context: ../services/auth + dockerfile: Dockerfile + restart: unless-stopped + env_file: .env + environment: + POSTGRES_URL: postgres://codelink:devpassword@postgres:5432/codelink + REDIS_URL: redis://redis:6379 + ports: + - "8081:8081" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + + relay-service: + build: + context: ../services/relay + dockerfile: Dockerfile + restart: unless-stopped + env_file: .env + environment: + POSTGRES_URL: postgres://codelink:devpassword@postgres:5432/codelink + REDIS_URL: redis://redis:6379 + ports: + - "8082:8082" + depends_on: + redis: + condition: service_healthy + +volumes: + pg_data: \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 97965d5..ceee904 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,16 @@ { "name": "codelink", - "version": "0.1.0", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codelink", - "version": "0.1.0", + "version": "2.0.0", "workspaces": [ - "packages/*" + "packages/protocol", + "packages/vscode-extension", + "packages/mobile" ], "devDependencies": { "@types/node": "^20.0.0", @@ -16,9 +18,7 @@ "@typescript-eslint/parser": "^6.0.0", "@vitest/coverage-v8": "^4.1.0", "@vitest/ui": "^4.1.0", - "babel-preset-expo": "^54.0.10", "eslint": "^8.0.0", - "fast-check": "^4.6.0", "prettier": "^3.0.0", "typescript": "^5.0.0", "vitest": "^4.1.0" @@ -38,31 +38,8 @@ } } }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/@babel/code-frame": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", @@ -75,14 +52,10 @@ }, "node_modules/@babel/code-frame/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/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.3", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -90,8 +63,6 @@ }, "node_modules/@babel/core": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", @@ -120,8 +91,6 @@ }, "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==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -129,8 +98,6 @@ }, "node_modules/@babel/generator": { "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "license": "MIT", "dependencies": { "@babel/parser": "^7.29.0", @@ -145,8 +112,6 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -157,8 +122,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", @@ -173,17 +136,13 @@ }, "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==", "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "version": "7.29.3", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -191,7 +150,7 @@ "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", + "@babel/traverse": "^7.29.0", "semver": "^6.3.1" }, "engines": { @@ -203,8 +162,6 @@ }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -238,8 +195,6 @@ }, "node_modules/@babel/helper-define-polyfill-provider": { "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", - "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", @@ -254,8 +209,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -263,8 +216,6 @@ }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.5", @@ -276,8 +227,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.6", @@ -289,8 +238,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -306,8 +253,6 @@ }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "license": "MIT", "dependencies": { "@babel/types": "^7.27.1" @@ -318,8 +263,6 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -344,8 +287,6 @@ }, "node_modules/@babel/helper-replace-supers": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", @@ -361,8 +302,6 @@ }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -374,8 +313,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -383,8 +320,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -392,8 +327,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -415,8 +348,6 @@ }, "node_modules/@babel/helpers": { "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", @@ -505,8 +436,6 @@ }, "node_modules/@babel/parser": { "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -520,8 +449,6 @@ }, "node_modules/@babel/plugin-proposal-decorators": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz", - "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==", "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", @@ -537,8 +464,6 @@ }, "node_modules/@babel/plugin-proposal-export-default-from": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.27.1.tgz", - "integrity": "sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -552,8 +477,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -564,8 +487,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -576,8 +497,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" @@ -588,8 +507,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -603,8 +520,6 @@ }, "node_modules/@babel/plugin-syntax-decorators": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz", - "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -630,8 +545,6 @@ }, "node_modules/@babel/plugin-syntax-export-default-from": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz", - "integrity": "sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -645,8 +558,6 @@ }, "node_modules/@babel/plugin-syntax-flow": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz", - "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -660,8 +571,6 @@ }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -675,8 +584,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -687,8 +594,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -699,8 +604,6 @@ }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -714,8 +617,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -726,8 +627,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -738,8 +637,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -750,8 +647,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -762,8 +657,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -774,8 +667,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -786,8 +677,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -801,8 +690,6 @@ }, "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==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -816,8 +703,6 @@ }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -911,8 +796,6 @@ }, "node_modules/@babel/plugin-transform-class-static-block": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", @@ -963,8 +846,6 @@ }, "node_modules/@babel/plugin-transform-destructuring": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -979,8 +860,6 @@ }, "node_modules/@babel/plugin-transform-export-namespace-from": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -994,8 +873,6 @@ }, "node_modules/@babel/plugin-transform-flow-strip-types": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz", - "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1073,8 +950,6 @@ }, "node_modules/@babel/plugin-transform-modules-commonjs": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", @@ -1135,8 +1010,6 @@ }, "node_modules/@babel/plugin-transform-object-rest-spread": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", @@ -1185,8 +1058,6 @@ }, "node_modules/@babel/plugin-transform-parameters": { "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1200,8 +1071,6 @@ }, "node_modules/@babel/plugin-transform-private-methods": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", @@ -1216,8 +1085,6 @@ }, "node_modules/@babel/plugin-transform-private-property-in-object": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -1233,8 +1100,6 @@ }, "node_modules/@babel/plugin-transform-react-display-name": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1248,8 +1113,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -1267,8 +1130,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx-development": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", "license": "MIT", "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" @@ -1312,8 +1173,6 @@ }, "node_modules/@babel/plugin-transform-react-pure-annotations": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -1343,8 +1202,6 @@ }, "node_modules/@babel/plugin-transform-runtime": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", - "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -1363,8 +1220,6 @@ }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1418,8 +1273,6 @@ }, "node_modules/@babel/plugin-transform-typescript": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", - "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -1453,8 +1306,6 @@ }, "node_modules/@babel/preset-react": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1473,8 +1324,6 @@ }, "node_modules/@babel/preset-typescript": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", - "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1492,8 +1341,6 @@ }, "node_modules/@babel/runtime": { "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1501,8 +1348,6 @@ }, "node_modules/@babel/template": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", @@ -1515,27 +1360,6 @@ }, "node_modules/@babel/traverse": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse--for-generate-function-map": { - "name": "@babel/traverse", - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", @@ -1552,8 +1376,6 @@ }, "node_modules/@babel/types": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -1565,186 +1387,36 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, - "node_modules/@callstack/react-theme-provider": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.9.tgz", - "integrity": "sha512-tTQ0uDSCL0ypeMa8T/E9wAZRGKWj8kXP7+6RYgPTfOPs9N07C9xM8P02GJ3feETap4Ux5S69D9nteq9mEj86NA==", - "license": "MIT", - "dependencies": { - "deepmerge": "^3.2.0", - "hoist-non-react-statics": "^3.3.0" - }, - "peerDependencies": { - "react": ">=16.3.0" - } - }, - "node_modules/@codelink/mobile-client": { - "resolved": "packages/mobile-client", - "link": true - }, "node_modules/@codelink/protocol": { "resolved": "packages/protocol", "link": true }, - "node_modules/@codelink/relay-server": { - "resolved": "packages/relay-server", - "link": true - }, - "node_modules/@csstools/color-helpers": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", - "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", - "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" + "@jridgewell/trace-mapping": "0.3.9" }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "node": ">=12" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1801,9 +1473,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -1834,136 +1506,94 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@expo-google-fonts/inter": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/inter/-/inter-0.2.3.tgz", - "integrity": "sha512-iHK9FI+dnE45X5c2Z5hSFwNH4zUWethizpbv3XUn0FIGw5jwvzriENz0a6wCdkI4/d+1QkurnHo5XHti7TbNJA==", - "license": "MIT" + "node_modules/@expo-google-fonts/material-symbols": { + "version": "0.4.38", + "resolved": "https://registry.npmjs.org/@expo-google-fonts/material-symbols/-/material-symbols-0.4.38.tgz", + "integrity": "sha512-IJkBtN1o8u9BW5fvSii1MyHPQ7Q0HxbWcVBvOrOzgMLpVtZw7R2w94wBTVR7kZwv3w1JNTESMmLA5Sqn1+Z36A==", + "license": "MIT AND Apache-2.0" }, - "node_modules/@expo-google-fonts/manrope": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/manrope/-/manrope-0.2.3.tgz", - "integrity": "sha512-2M9hzi5ku97ZbheGMyzqIdiIEmve0/ihBk9nVWRy2lVIchWqR2k2yIQPS7jKC/Az/e43tW3POLNeU6gQJQL9hw==", - "license": "MIT" + "node_modules/@expo/apple-utils": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/@expo/apple-utils/-/apple-utils-2.1.19.tgz", + "integrity": "sha512-f1iMteL+tTOSF1sovVB35ncobdiZvhjWvwEOWGIuAutyeIpcxNJ/2tUZSE748X/VEQLn1cL2Tozkdp2MLXStvA==", + "license": "MIT", + "bin": { + "apple-utils": "bin.js" + } }, - "node_modules/@expo-google-fonts/space-grotesk": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/space-grotesk/-/space-grotesk-0.2.3.tgz", - "integrity": "sha512-UYEMIrzegR02pauH7gVMI7j6cUroTtJug6dH/aQFjMNz0UwZe6GUcrEtJDmsUHJjEZdxbYgHhaiIwswWVo0CMA==", - "license": "MIT" + "node_modules/@expo/bunyan": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.1.tgz", + "integrity": "sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==", + "license": "MIT", + "dependencies": { + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@expo/bunyan/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==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/@expo/cli": { - "version": "54.0.23", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.23.tgz", - "integrity": "sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==", + "node_modules/@expo/code-signing-certificates": { + "version": "0.0.6", "license": "MIT", "dependencies": { - "@0no-co/graphql.web": "^1.0.8", - "@expo/code-signing-certificates": "^0.0.6", - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/devcert": "^1.2.1", - "@expo/env": "~2.0.8", - "@expo/image-utils": "^0.8.8", - "@expo/json-file": "^10.0.8", - "@expo/metro": "~54.2.0", - "@expo/metro-config": "~54.0.14", - "@expo/osascript": "^2.3.8", - "@expo/package-manager": "^1.9.10", - "@expo/plist": "^0.4.8", - "@expo/prebuild-config": "^54.0.8", - "@expo/schema-utils": "^0.1.8", - "@expo/spawn-async": "^1.7.2", - "@expo/ws-tunnel": "^1.0.1", - "@expo/xcpretty": "^4.3.0", - "@react-native/dev-middleware": "0.81.5", - "@urql/core": "^5.0.6", - "@urql/exchange-retry": "^1.3.0", - "accepts": "^1.3.8", - "arg": "^5.0.2", - "better-opn": "~3.0.2", - "bplist-creator": "0.1.0", - "bplist-parser": "^0.3.1", - "chalk": "^4.0.0", - "ci-info": "^3.3.0", - "compression": "^1.7.4", - "connect": "^3.7.0", - "debug": "^4.3.4", - "env-editor": "^0.4.1", - "expo-server": "^1.0.5", - "freeport-async": "^2.0.0", + "node-forge": "^1.3.3" + } + }, + "node_modules/@expo/config": { + "version": "55.0.17", + "license": "MIT", + "dependencies": { + "@expo/config-plugins": "~55.0.9", + "@expo/config-types": "^55.0.5", + "@expo/json-file": "^10.0.14", + "@expo/require-utils": "^55.0.5", + "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", - "lan-network": "^0.1.6", - "minimatch": "^9.0.0", - "node-forge": "^1.3.3", - "npm-package-arg": "^11.0.0", - "ora": "^3.4.0", - "picomatch": "^3.0.1", - "pretty-bytes": "^5.6.0", - "pretty-format": "^29.7.0", - "progress": "^2.0.3", - "prompts": "^2.3.2", - "qrcode-terminal": "0.11.0", - "require-from-string": "^2.0.2", - "requireg": "^0.2.2", - "resolve": "^1.22.2", - "resolve-from": "^5.0.0", - "resolve.exports": "^2.0.3", + "resolve-workspace-root": "^2.0.0", "semver": "^7.6.0", - "send": "^0.19.0", - "slugify": "^1.3.4", - "source-map-support": "~0.5.21", - "stacktrace-parser": "^0.1.10", - "structured-headers": "^0.4.1", - "tar": "^7.5.2", - "terminal-link": "^2.1.1", - "undici": "^6.18.2", - "wrap-ansi": "^7.0.0", - "ws": "^8.12.1" - }, - "bin": { - "expo-internal": "build/bin/cli" - }, - "peerDependencies": { - "expo": "*", - "expo-router": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "expo-router": { - "optional": true - }, - "react-native": { - "optional": true - } + "slugify": "^1.3.4" } }, - "node_modules/@expo/cli/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==", + "node_modules/@expo/config-plugins": { + "version": "55.0.9", "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "dependencies": { + "@expo/config-types": "^55.0.5", + "@expo/json-file": "~10.0.14", + "@expo/plist": "^0.5.3", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" } }, - "node_modules/@expo/cli/node_modules/balanced-match": { + "node_modules/@expo/config-plugins/node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, - "node_modules/@expo/cli/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/@expo/config-plugins/node_modules/brace-expansion": { + "version": "5.0.6", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -1972,10 +1602,8 @@ "node": "18 || 20 || >=22" } }, - "node_modules/@expo/cli/node_modules/glob": { + "node_modules/@expo/config-plugins/node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -1989,10 +1617,8 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@expo/cli/node_modules/glob/node_modules/minimatch": { + "node_modules/@expo/config-plugins/node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -2004,91 +1630,19 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@expo/cli/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==", - "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/@expo/cli/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==", + "node_modules/@expo/config-types": { + "version": "55.0.5", "license": "MIT" }, - "node_modules/@expo/code-signing-certificates": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz", - "integrity": "sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==", - "license": "MIT", - "dependencies": { - "node-forge": "^1.3.3" - } - }, - "node_modules/@expo/config": { - "version": "12.0.13", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.13.tgz", - "integrity": "sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "@expo/config-plugins": "~54.0.4", - "@expo/config-types": "^54.0.10", - "@expo/json-file": "^10.0.8", - "deepmerge": "^4.3.1", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0", - "resolve-workspace-root": "^2.0.0", - "semver": "^7.6.0", - "slugify": "^1.3.4", - "sucrase": "~3.35.1" - } - }, - "node_modules/@expo/config-plugins": { - "version": "54.0.4", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.4.tgz", - "integrity": "sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==", - "license": "MIT", - "dependencies": { - "@expo/config-types": "^54.0.10", - "@expo/json-file": "~10.0.8", - "@expo/plist": "^0.4.8", - "@expo/sdk-runtime-versions": "^1.0.0", - "chalk": "^4.1.2", - "debug": "^4.3.5", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.5.4", - "slash": "^3.0.0", - "slugify": "^1.6.6", - "xcode": "^3.0.1", - "xml2js": "0.6.0" - } - }, - "node_modules/@expo/config-plugins/node_modules/balanced-match": { + "node_modules/@expo/config/node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, - "node_modules/@expo/config-plugins/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/@expo/config/node_modules/brace-expansion": { + "version": "5.0.6", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -2097,10 +1651,8 @@ "node": "18 || 20 || >=22" } }, - "node_modules/@expo/config-plugins/node_modules/glob": { + "node_modules/@expo/config/node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -2114,10 +1666,8 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@expo/config-plugins/node_modules/minimatch": { + "node_modules/@expo/config/node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -2129,142 +1679,248 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@expo/config-types": { - "version": "54.0.10", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.10.tgz", - "integrity": "sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==", - "license": "MIT" + "node_modules/@expo/devcert": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "@expo/sudo-prompt": "^9.3.1", + "debug": "^3.1.0" + } }, - "node_modules/@expo/config/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "node_modules/@expo/devcert/node_modules/debug": { + "version": "3.2.7", "license": "MIT", "dependencies": { - "@babel/highlight": "^7.10.4" + "ms": "^2.1.1" } }, - "node_modules/@expo/config/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@expo/devtools": { + "version": "55.0.3", "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" + "dependencies": { + "chalk": "^4.1.2" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-native": { + "optional": true + } } }, - "node_modules/@expo/config/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/@expo/dom-webview": { + "version": "55.0.6", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/@expo/eas-build-job": { + "version": "18.13.1", + "resolved": "https://registry.npmjs.org/@expo/eas-build-job/-/eas-build-job-18.13.1.tgz", + "integrity": "sha512-4AA6rulUwhmIE1mtpbfzz7uda8xuMKjuhtueBzh7bvRMx1Ph8vqLkyR6vCD9SGeKcMqJTa2sQsrEfwhqjcBljw==", "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "@expo/logger": "18.5.0", + "@expo/results": "1.0.0", + "@expo/turtle-spawn": "18.5.0", + "joi": "^17.13.1", + "semver": "^7.6.2", + "zod": "^4.3.5" + } + }, + "node_modules/@expo/eas-build-job/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/@expo/eas-build-job/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@expo/eas-json": { + "version": "18.13.1", + "resolved": "https://registry.npmjs.org/@expo/eas-json/-/eas-json-18.13.1.tgz", + "integrity": "sha512-kbDAiYpwn+n6UjtSFmy0PsFm8caJ4u92GsHuIrtuy+hCzOfPhS+mp1GkSiFDAFmK75iS3x83flwxAMTEnvv9rw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.23.5", + "@expo/eas-build-job": "18.13.1", + "chalk": "4.1.2", + "env-string": "1.0.1", + "fs-extra": "11.2.0", + "golden-fleece": "1.0.9", + "joi": "17.11.0", + "log-symbols": "4.1.0", + "semver": "7.5.2", + "terminal-link": "2.1.1", + "tslib": "2.4.1" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=20.0.0" } }, - "node_modules/@expo/config/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==", + "node_modules/@expo/eas-json/node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/@expo/config/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", + "node_modules/@expo/eas-json/node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=4" + } + }, + "node_modules/@expo/eas-json/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=4" } }, - "node_modules/@expo/config/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", + "node_modules/@expo/eas-json/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@expo/eas-json/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@expo/eas-json/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.5" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@expo/devcert": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.1.tgz", - "integrity": "sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==", - "license": "MIT", + "node_modules/@expo/eas-json/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", "dependencies": { - "@expo/sudo-prompt": "^9.3.1", - "debug": "^3.1.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@expo/devcert/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", + "node_modules/@expo/eas-json/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "license": "ISC", "dependencies": { - "ms": "^2.1.1" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@expo/devtools": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-0.1.8.tgz", - "integrity": "sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ==", + "node_modules/@expo/eas-json/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "license": "MIT", "dependencies": { - "chalk": "^4.1.2" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" + "has-flag": "^3.0.0" }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-native": { - "optional": true - } + "engines": { + "node": ">=4" } }, + "node_modules/@expo/eas-json/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "license": "0BSD" + }, + "node_modules/@expo/eas-json/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/@expo/env": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.11.tgz", - "integrity": "sha512-xV+ps6YCW7XIPVUwFVCRN2nox09dnRwy8uIjwHWTODu0zFw4kp4omnVkl0OOjuu2XOe7tdgAHxikrkJt9xB/7Q==", + "version": "2.1.2", "license": "MIT", "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", "getenv": "^2.0.0" + }, + "engines": { + "node": ">=20.12.0" } }, "node_modules/@expo/fingerprint": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.15.4.tgz", - "integrity": "sha512-eYlxcrGdR2/j2M6pEDXo9zU9KXXF1vhP+V+Tl+lyY+bU8lnzrN6c637mz6Ye3em2ANy8hhUR03Raf8VsT9Ogng==", + "version": "0.16.7", "license": "MIT", "dependencies": { + "@expo/env": "^2.1.2", "@expo/spawn-async": "^1.7.2", "arg": "^5.0.2", "chalk": "^4.1.2", @@ -2272,8 +1928,7 @@ "getenv": "^2.0.0", "glob": "^13.0.0", "ignore": "^5.3.1", - "minimatch": "^9.0.0", - "p-limit": "^3.1.0", + "minimatch": "^10.2.2", "resolve-from": "^5.0.0", "semver": "^7.6.0" }, @@ -2283,17 +1938,13 @@ }, "node_modules/@expo/fingerprint/node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@expo/fingerprint/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -2304,8 +1955,6 @@ }, "node_modules/@expo/fingerprint/node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -2319,10 +1968,8 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@expo/fingerprint/node_modules/glob/node_modules/minimatch": { + "node_modules/@expo/fingerprint/node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -2335,77 +1982,100 @@ } }, "node_modules/@expo/image-utils": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.12.tgz", - "integrity": "sha512-3KguH7kyKqq7pNwLb9j6BBdD/bjmNwXZG/HPWT6GWIXbwrvAJt2JNyYTP5agWJ8jbbuys1yuCzmkX+TU6rmI7A==", + "version": "0.8.14", "license": "MIT", "dependencies": { + "@expo/require-utils": "^55.0.5", "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", "getenv": "^2.0.0", "jimp-compact": "0.16.1", "parse-png": "^2.1.0", - "resolve-from": "^5.0.0", "semver": "^7.6.0" } }, "node_modules/@expo/json-file": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.13.tgz", - "integrity": "sha512-pX/XjQn7tgNw6zuuV2ikmegmwe/S7uiwhrs2wXrANMkq7ozrA+JcZwgW9Q/8WZgciBzfAhNp5hnackHcrmapQA==", + "version": "10.0.14", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.20.0", "json5": "^2.2.3" } }, + "node_modules/@expo/local-build-cache-provider": { + "version": "55.0.13", + "license": "MIT", + "dependencies": { + "@expo/config": "~55.0.17", + "chalk": "^4.1.2" + } + }, + "node_modules/@expo/log-box": { + "version": "55.0.12", + "license": "MIT", + "dependencies": { + "@expo/dom-webview": "^55.0.6", + "anser": "^1.4.9", + "stacktrace-parser": "^0.1.10" + }, + "peerDependencies": { + "@expo/dom-webview": "^55.0.6", + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/@expo/logger": { + "version": "18.5.0", + "resolved": "https://registry.npmjs.org/@expo/logger/-/logger-18.5.0.tgz", + "integrity": "sha512-KYMvfgsgkBQOaWVILokKJBhtuY9My4WozTm+N9tyjNvJ5eg23DhqWIvJpHnneg+KHLJaeDjbtaQ1/YkFreHU5g==", + "license": "BUSL-1.1", + "dependencies": { + "@types/bunyan": "^1.8.11", + "bunyan": "^1.8.15" + } + }, "node_modules/@expo/metro": { - "version": "54.2.0", - "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-54.2.0.tgz", - "integrity": "sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w==", - "license": "MIT", - "dependencies": { - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3" + "version": "55.1.1", + "license": "MIT", + "dependencies": { + "metro": "0.83.7", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-config": "0.83.7", + "metro-core": "0.83.7", + "metro-file-map": "0.83.7", + "metro-minify-terser": "0.83.7", + "metro-resolver": "0.83.7", + "metro-runtime": "0.83.7", + "metro-source-map": "0.83.7", + "metro-symbolicate": "0.83.7", + "metro-transform-plugins": "0.83.7", + "metro-transform-worker": "0.83.7" } }, "node_modules/@expo/metro-config": { - "version": "54.0.14", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.14.tgz", - "integrity": "sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA==", + "version": "55.0.21", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", - "@expo/config": "~12.0.13", - "@expo/env": "~2.0.8", - "@expo/json-file": "~10.0.8", - "@expo/metro": "~54.2.0", + "@expo/config": "~55.0.17", + "@expo/env": "~2.1.2", + "@expo/json-file": "~10.0.14", + "@expo/metro": "~55.1.1", "@expo/spawn-async": "^1.7.2", "browserslist": "^4.25.0", "chalk": "^4.1.0", "debug": "^4.3.2", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", "getenv": "^2.0.0", "glob": "^13.0.0", - "hermes-parser": "^0.29.1", + "hermes-parser": "^0.32.0", "jsc-safe-url": "^0.2.4", "lightningcss": "^1.30.1", - "minimatch": "^9.0.0", + "picomatch": "^4.0.3", "postcss": "~8.4.32", "resolve-from": "^5.0.0" }, @@ -2420,17 +2090,13 @@ }, "node_modules/@expo/metro-config/node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@expo/metro-config/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -2441,8 +2107,6 @@ }, "node_modules/@expo/metro-config/node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -2456,10 +2120,19 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@expo/metro-config/node_modules/glob/node_modules/minimatch": { + "node_modules/@expo/metro-config/node_modules/hermes-estree": { + "version": "0.32.1", + "license": "MIT" + }, + "node_modules/@expo/metro-config/node_modules/hermes-parser": { + "version": "0.32.1", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.32.1" + } + }, + "node_modules/@expo/metro-config/node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -2471,10 +2144,254 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@expo/metro-config/node_modules/postcss": { + "version": "8.4.49", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@expo/metro-runtime": { + "version": "55.0.11", + "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-55.0.11.tgz", + "integrity": "sha512-4KKi/jGrIEXi2YGu0hYTVr0CEeRJy5SXbCrz9+KDZkuD3ROwKNpM1DBawni5rhPVovFnR323HBck9GaxhnfrRw==", + "license": "MIT", + "dependencies": { + "@expo/log-box": "55.0.12", + "anser": "^1.4.9", + "pretty-format": "^29.7.0", + "stacktrace-parser": "^0.1.10", + "whatwg-fetch": "^3.0.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-dom": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@expo/multipart-body-parser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@expo/multipart-body-parser/-/multipart-body-parser-2.0.0.tgz", + "integrity": "sha512-yS/wsqlj0d8ZKETEN7ro3dZtjdMhpte8wp+xUzjUQC3jizxcE0E62xgvGquJObiYUMGoCF5qRYr2t78STPEaSw==", + "license": "MIT", + "dependencies": { + "multipasta": "^0.2.5" + } + }, + "node_modules/@expo/ngrok": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@expo/ngrok/-/ngrok-4.1.3.tgz", + "integrity": "sha512-AESYaROGIGKWwWmUyQoUXcbvaUZjmpecC5buArXxYou+RID813F8T0Y5jQ2HUY49mZpYfJiy9oh4VSN37GgrXA==", + "license": "BSD-2-Clause", + "dependencies": { + "@expo/ngrok-bin": "2.3.42", + "got": "^11.5.1", + "uuid": "^3.3.2", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/@expo/ngrok-bin": { + "version": "2.3.42", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin/-/ngrok-bin-2.3.42.tgz", + "integrity": "sha512-kyhORGwv9XpbPeNIrX6QZ9wDVCDOScyTwxeS+ScNmUqYoZqD9LRmEqF7bpDh5VonTsrXgWrGl7wD2++oSHcaTQ==", + "bin": { + "ngrok": "bin/ngrok.js" + }, + "optionalDependencies": { + "@expo/ngrok-bin-darwin-arm64": "2.3.41", + "@expo/ngrok-bin-darwin-x64": "2.3.41", + "@expo/ngrok-bin-freebsd-ia32": "2.3.41", + "@expo/ngrok-bin-freebsd-x64": "2.3.41", + "@expo/ngrok-bin-linux-arm": "2.3.41", + "@expo/ngrok-bin-linux-arm64": "2.3.41", + "@expo/ngrok-bin-linux-ia32": "2.3.41", + "@expo/ngrok-bin-linux-x64": "2.3.41", + "@expo/ngrok-bin-sunos-x64": "2.3.41", + "@expo/ngrok-bin-win32-ia32": "2.3.41", + "@expo/ngrok-bin-win32-x64": "2.3.41" + } + }, + "node_modules/@expo/ngrok-bin-darwin-arm64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-arm64/-/ngrok-bin-darwin-arm64-2.3.41.tgz", + "integrity": "sha512-TPf95xp6SkvbRONZjltTOFcCJbmzAH7lrQ36Dv+djrOckWGPVq4HCur48YAeiGDqspmFEmqZ7ykD5c/bDfRFOA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@expo/ngrok-bin-darwin-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-x64/-/ngrok-bin-darwin-x64-2.3.41.tgz", + "integrity": "sha512-29QZHfX4Ec0p0pQF5UrqiP2/Qe7t2rI96o+5b8045VCEl9AEAKHceGuyo+jfUDR4FSQBGFLSDb06xy8ghL3ZYA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@expo/ngrok-bin-freebsd-ia32": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-ia32/-/ngrok-bin-freebsd-ia32-2.3.41.tgz", + "integrity": "sha512-YYXgwNZ+p0aIrwgb+1/RxJbsWhGEzBDBhZulKg1VB7tKDAd2C8uGnbK1rOCuZy013iOUsJDXaj9U5QKc13iIXw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@expo/ngrok-bin-freebsd-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-x64/-/ngrok-bin-freebsd-x64-2.3.41.tgz", + "integrity": "sha512-1Ei6K8BB+3etmmBT0tXYC4dyVkJMigT4ELbRTF5jKfw1pblqeXM9Qpf3p8851PTlH142S3bockCeO39rSkOnkg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@expo/ngrok-bin-linux-arm": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm/-/ngrok-bin-linux-arm-2.3.41.tgz", + "integrity": "sha512-B6+rW/+tEi7ZrKWQGkRzlwmKo7c1WJhNODFBSgkF/Sj9PmmNhBz67mer91S2+6nNt5pfcwLLd61CjtWfR1LUHQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-arm64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm64/-/ngrok-bin-linux-arm64-2.3.41.tgz", + "integrity": "sha512-eC8GA/xPcmQJy4h+g2FlkuQB3lf5DjITy8Y6GyydmPYMByjUYAGEXe0brOcP893aalAzRqbNOAjSuAw1lcCLSQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-ia32": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-ia32/-/ngrok-bin-linux-ia32-2.3.41.tgz", + "integrity": "sha512-w5Cy31wSz4jYnygEHS7eRizR1yt8s9TX6kHlkjzayIiRTFRb2E1qD2l0/4T2w0LJpBjM5ZFPaaKqsNWgCUIEow==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-x64/-/ngrok-bin-linux-x64-2.3.41.tgz", + "integrity": "sha512-LcU3MbYHv7Sn2eFz8Yzo2rXduufOvX1/hILSirwCkH+9G8PYzpwp2TeGqVWuO+EmvtBe6NEYwgdQjJjN6I4L1A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-sunos-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-sunos-x64/-/ngrok-bin-sunos-x64-2.3.41.tgz", + "integrity": "sha512-bcOj45BLhiV2PayNmLmEVZlFMhEiiGpOr36BXC0XSL+cHUZHd6uNaS28AaZdz95lrRzGpeb0hAF8cuJjo6nq4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/@expo/ngrok-bin-win32-ia32": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-ia32/-/ngrok-bin-win32-ia32-2.3.41.tgz", + "integrity": "sha512-0+vPbKvUA+a9ERgiAknmZCiWA3AnM5c6beI+51LqmjKEM4iAAlDmfXNJ89aAbvZMUtBNwEPHzJHnaM4s2SeBhA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@expo/ngrok-bin-win32-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-x64/-/ngrok-bin-win32-x64-2.3.41.tgz", + "integrity": "sha512-mncsPRaG462LiYrM8mQT8OYe3/i44m3N/NzUeieYpGi8+pCOo8TIC23kR9P93CVkbM9mmXsy3X6hq91a8FWBdA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@expo/ngrok/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/@expo/ngrok/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/@expo/osascript": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.4.2.tgz", - "integrity": "sha512-/XP7PSYF2hzOZzqfjgkoWtllyeTN8dW3aM4P6YgKcmmPikKL5FdoyQhti4eh6RK5a5VrUXJTOlTNIpIHsfB5Iw==", + "version": "2.4.3", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.7.2" @@ -2484,12 +2401,10 @@ } }, "node_modules/@expo/package-manager": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.10.4.tgz", - "integrity": "sha512-y9Mr4Kmpk4abAVZrNNPCdzOZr8nLLyi18p1SXr0RCVA8IfzqZX/eY4H+50a0HTmXqIsPZrQdcdb4I3ekMS9GvQ==", + "version": "1.10.5", "license": "MIT", "dependencies": { - "@expo/json-file": "^10.0.13", + "@expo/json-file": "^10.0.14", "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", "npm-package-arg": "^11.0.0", @@ -2497,91 +2412,383 @@ "resolve-workspace-root": "^2.0.0" } }, + "node_modules/@expo/pkcs12": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@expo/pkcs12/-/pkcs12-0.1.3.tgz", + "integrity": "sha512-96MePEGppKi08vawrTPw8kMCRdsbrDbV900MlI8rrP9F57DfDl/y1P52bwIDBYCEHE3XtPMo7s1xkG0BKOLCVg==", + "license": "MIT", + "dependencies": { + "node-forge": "^1.2.1" + } + }, "node_modules/@expo/plist": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.8.tgz", - "integrity": "sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ==", + "version": "0.5.3", "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.2.3", + "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, - "node_modules/@expo/prebuild-config": { - "version": "54.0.8", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-54.0.8.tgz", - "integrity": "sha512-EA7N4dloty2t5Rde+HP0IEE+nkAQiu4A/+QGZGT9mFnZ5KKjPPkqSyYcRvP5bhQE10D+tvz6X0ngZpulbMdbsg==", + "node_modules/@expo/plugin-help": { + "version": "5.1.23", + "resolved": "https://registry.npmjs.org/@expo/plugin-help/-/plugin-help-5.1.23.tgz", + "integrity": "sha512-s0uH6cPplLj73ZVie40EYUhl7X7q9kRR+8IfZWDod3wUtVGOFInxuCPX9Jpv1UwwBgbRu2cLisqr8m45LrFgxw==", "license": "MIT", "dependencies": { - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/config-types": "^54.0.10", - "@expo/image-utils": "^0.8.8", - "@expo/json-file": "^10.0.8", - "@react-native/normalize-colors": "0.81.5", - "debug": "^4.3.1", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "xml2js": "0.6.0" + "@oclif/core": "^2.11.1" }, - "peerDependencies": { - "expo": "*" + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@expo/require-utils": { - "version": "55.0.3", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.3.tgz", - "integrity": "sha512-TS1m5tW45q4zoaTlt6DwmdYHxvFTIxoLrTHKOFrIirHIqIXnHCzpceg8wumiBi+ZXSaGY2gobTbfv+WVhJY6Fw==", + "node_modules/@expo/plugin-help/node_modules/@oclif/core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", + "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0" + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@expo/schema-utils": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.8.tgz", - "integrity": "sha512-9I6ZqvnAvKKDiO+ZF8BpQQFYWXOJvTAL5L/227RUbWG1OVZDInFifzCBiqAZ3b67NRfeAgpgvbA7rejsqhY62A==", - "license": "MIT" + "node_modules/@expo/plugin-help/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } }, - "node_modules/@expo/sdk-runtime-versions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", - "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", - "license": "MIT" + "node_modules/@expo/plugin-help/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } }, - "node_modules/@expo/spawn-async": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", - "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", + "node_modules/@expo/plugin-help/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==", "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@expo/sudo-prompt": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz", - "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", - "license": "MIT" - }, - "node_modules/@expo/vector-icons": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-15.1.1.tgz", - "integrity": "sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw==", + "node_modules/@expo/plugin-warn-if-update-available": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@expo/plugin-warn-if-update-available/-/plugin-warn-if-update-available-2.5.1.tgz", + "integrity": "sha512-B65QSIZ+TgFHnVXsTw+1Q6djsJByWwnIjYfoG8ZV9wizOC01gbAw1cOZ/YtrJ2BrDnzFQtM8qecjlmZ7C3MPLw==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^2.11.1", + "chalk": "^4.1.0", + "debug": "^4.3.4", + "ejs": "^3.1.7", + "fs-extra": "^10.1.0", + "http-call": "^5.2.2", + "semver": "^7.3.7", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/@oclif/core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", + "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "license": "MIT", + "dependencies": { + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@expo/plugin-warn-if-update-available/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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@expo/prebuild-config": { + "version": "55.0.18", + "license": "MIT", + "dependencies": { + "@expo/config": "~55.0.17", + "@expo/config-plugins": "~55.0.9", + "@expo/config-types": "^55.0.5", + "@expo/image-utils": "^0.8.14", + "@expo/json-file": "^10.0.14", + "@react-native/normalize-colors": "0.83.6", + "debug": "^4.3.1", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "xml2js": "0.6.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/@expo/require-utils": { + "version": "55.0.5", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "@babel/core": "^7.25.2", + "@babel/plugin-transform-modules-commonjs": "^7.24.8" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@expo/results": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@expo/results/-/results-1.0.0.tgz", + "integrity": "sha512-qECzzXX5oJot3m2Gu9pfRDz50USdBieQVwYAzeAtQRUTD3PVeTK1tlRUoDcrK8PSruDLuVYdKkLebX4w/o55VA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@expo/rudder-sdk-node": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.1.tgz", + "integrity": "sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==", + "license": "MIT", + "dependencies": { + "@expo/bunyan": "^4.0.0", + "@segment/loosely-validate-event": "^2.0.0", + "fetch-retry": "^4.1.1", + "md5": "^2.2.1", + "node-fetch": "^2.6.1", + "remove-trailing-slash": "^0.1.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/rudder-sdk-node/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==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@expo/schema-utils": { + "version": "55.0.4", + "license": "MIT" + }, + "node_modules/@expo/sdk-runtime-versions": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/@expo/spawn-async": { + "version": "1.7.2", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/steps": { + "version": "18.13.1", + "resolved": "https://registry.npmjs.org/@expo/steps/-/steps-18.13.1.tgz", + "integrity": "sha512-V3ush+Jq23qd0ExOvRwTuBJ/HV717il6VMdANapWoM1hUWhyKsNypaF6DMKYejawnGClddrrGO8hNeQ7NfYgKw==", + "license": "BUSL-1.1", + "dependencies": { + "@expo/eas-build-job": "18.13.1", + "@expo/logger": "18.5.0", + "@expo/spawn-async": "^1.7.2", + "arg": "^5.0.2", + "fs-extra": "^11.2.0", + "joi": "^17.13.1", + "jsep": "^1.3.8", + "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", + "uuid": "^9.0.1", + "yaml": "^2.4.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@expo/steps/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/@expo/steps/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@expo/sudo-prompt": { + "version": "9.3.2", + "license": "MIT" + }, + "node_modules/@expo/timeago.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@expo/timeago.js/-/timeago.js-1.0.0.tgz", + "integrity": "sha512-PD45CGlCL8kG0U3YcH1NvYxQThw5XAS7qE9bgP4L7dakm8lsMz+p8BQ1IjBFMmImawVWsV3py6JZINaEebXLnw==", + "license": "MIT" + }, + "node_modules/@expo/turtle-spawn": { + "version": "18.5.0", + "resolved": "https://registry.npmjs.org/@expo/turtle-spawn/-/turtle-spawn-18.5.0.tgz", + "integrity": "sha512-ZVDlE/GyuGJBzkSOrnbNWIAcPrtOuDybrMNvBLO8kTIQXZhHjsHn8c4kcG29JyI/er+9J+15OoaEtUAYQw+S3g==", + "license": "BUSL-1.1", + "dependencies": { + "@expo/logger": "18.5.0", + "@expo/spawn-async": "^1.7.2" + } + }, + "node_modules/@expo/vector-icons": { + "version": "15.1.1", "license": "MIT", "peerDependencies": { "expo-font": ">=14.0.4", @@ -2591,14 +2798,10 @@ }, "node_modules/@expo/ws-tunnel": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", - "integrity": "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==", "license": "MIT" }, "node_modules/@expo/xcpretty": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.2.tgz", - "integrity": "sha512-ni4WJrmmY43GB1d++iTB7OD7JQlwGarrdhS7pb/ibJKrRIf/HOeuTZndPalXM2QOD7JpfZ/51QprRU9UOjTROw==", + "version": "4.4.4", "license": "BSD-3-Clause", "dependencies": { "@babel/code-frame": "^7.20.0", @@ -2609,6 +2812,21 @@ "excpretty": "build/cli.js" } }, + "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", @@ -2626,9 +2844,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -2671,6 +2889,102 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -2685,8 +2999,6 @@ }, "node_modules/@isaacs/ttlcache": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", - "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", "license": "ISC", "engines": { "node": ">=12" @@ -2694,8 +3006,6 @@ }, "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==", "license": "ISC", "dependencies": { "camelcase": "^5.3.1", @@ -2710,8 +3020,6 @@ }, "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==", "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -2719,8 +3027,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "license": "MIT", "engines": { "node": ">=6" @@ -2728,8 +3034,6 @@ }, "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==", "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -2741,8 +3045,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -2754,8 +3056,6 @@ }, "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==", "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -2766,8 +3066,6 @@ }, "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==", "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -2781,8 +3079,6 @@ }, "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==", "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -2792,9 +3088,7 @@ } }, "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==", + "version": "0.1.6", "license": "MIT", "engines": { "node": ">=8" @@ -2802,8 +3096,6 @@ }, "node_modules/@jest/create-cache-key-function": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", - "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", "license": "MIT", "dependencies": { "@jest/types": "^29.6.3" @@ -2812,20 +3104,8 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "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==", "license": "MIT", "dependencies": { "@jest/fake-timers": "^29.7.0", @@ -2839,8 +3119,6 @@ }, "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==", "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -2854,20 +3132,8 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "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==", "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -2878,8 +3144,6 @@ }, "node_modules/@jest/transform": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", @@ -2904,8 +3168,6 @@ }, "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==", "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -2921,8 +3183,6 @@ }, "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", @@ -2931,8 +3191,6 @@ }, "node_modules/@jridgewell/remapping": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2941,8 +3199,6 @@ }, "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" @@ -2950,8 +3206,6 @@ }, "node_modules/@jridgewell/source-map": { "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2960,14 +3214,10 @@ }, "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.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2976,8 +3226,6 @@ }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", "license": "MIT", "dependencies": { "debug": "^4.1.1" @@ -2985,34 +3233,10 @@ }, "node_modules/@kwsites/promise-deferred": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT" }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", - "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "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", @@ -3024,9 +3248,6 @@ }, "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" @@ -3034,9 +3255,6 @@ }, "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", @@ -3046,979 +3264,1420 @@ "node": ">= 8" } }, - "node_modules/@oxc-project/types": { - "version": "0.124.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", - "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, - "license": "MIT" - }, - "node_modules/@react-native-async-storage/async-storage": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", - "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==", + "node_modules/@oclif/core": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.11.3.tgz", + "integrity": "sha512-gQCSYAtUhJilGKaSaZhqejH9X1dDu+jWQjLmtGOgN/XcKaAEPPSeT2mu1UvlvtPox1/NNRdlBcUa8KRKo2HnJQ==", "license": "MIT", "dependencies": { - "merge-options": "^3.0.4" + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.8.0", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.16", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" }, - "peerDependencies": { - "react-native": "^0.0.0-0 || >=0.65 <1.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@react-native/assets-registry": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz", - "integrity": "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==", + "node_modules/@oclif/core/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { - "node": ">= 20.19.4" + "node": "18 || 20 || >=22" } }, - "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.81.5.tgz", - "integrity": "sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ==", + "node_modules/@oclif/core/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.3", - "@react-native/codegen": "0.81.5" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">= 20.19.4" + "node": "18 || 20 || >=22" } }, - "node_modules/@react-native/babel-preset": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.81.5.tgz", - "integrity": "sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA==", - "license": "MIT", + "node_modules/@oclif/core/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.25.2", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/template": "^7.25.0", - "@react-native/babel-plugin-codegen": "0.81.5", - "babel-plugin-syntax-hermes-parser": "0.29.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.14.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">= 20.19.4" + "node": "18 || 20 || >=22" }, - "peerDependencies": { - "@babel/core": "*" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@react-native/codegen": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.5.tgz", - "integrity": "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g==", + "node_modules/@oclif/core/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==", "license": "MIT", "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.25.3", - "glob": "^7.1.1", - "hermes-parser": "0.29.1", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 20.19.4" + "node": ">=10" }, - "peerDependencies": { - "@babel/core": "*" + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@react-native/community-cli-plugin": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.5.tgz", - "integrity": "sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw==", + "node_modules/@oclif/plugin-autocomplete": { + "version": "3.2.49", + "resolved": "https://registry.npmjs.org/@oclif/plugin-autocomplete/-/plugin-autocomplete-3.2.49.tgz", + "integrity": "sha512-+rrAZ468bW/B9uVrn6sEnFYepy3M1N/BWht8mHzhFIFCIduPSoE+8MweROxZLOGBZrXGWt0iavuPQmy0eaXRfQ==", "license": "MIT", "dependencies": { - "@react-native/dev-middleware": "0.81.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "metro": "^0.83.1", - "metro-config": "^0.83.1", - "metro-core": "^0.83.1", - "semver": "^7.1.3" + "@oclif/core": "^4", + "ansis": "^3.16.0", + "debug": "^4.4.1", + "ejs": "^3.1.10" }, "engines": { - "node": ">= 20.19.4" + "node": ">=18.0.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.124.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "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", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { - "@react-native-community/cli": "*", - "@react-native/metro-config": "*" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "@react-native-community/cli": { + "@types/react": { "optional": true }, - "@react-native/metro-config": { + "@types/react-dom": { "optional": true } } }, - "node_modules/@react-native/debugger-frontend": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.5.tgz", - "integrity": "sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/dev-middleware": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.81.5.tgz", - "integrity": "sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA==", + "node_modules/@radix-ui/react-collection/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": { - "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.81.5", - "chrome-launcher": "^0.15.2", - "chromium-edge-launcher": "^0.2.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "open": "^7.0.3", - "serve-static": "^1.16.2", - "ws": "^6.2.3" + "@radix-ui/react-compose-refs": "1.1.2" }, - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/dev-middleware/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-native/gradle-plugin": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz", - "integrity": "sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg==", + "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", - "engines": { - "node": ">= 20.19.4" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-native/js-polyfills": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.5.tgz", - "integrity": "sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w==", + "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", - "engines": { - "node": ">= 20.19.4" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-native/normalize-colors": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz", - "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", - "license": "MIT" - }, - "node_modules/@react-native/virtualized-lists": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", - "integrity": "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw==", + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "license": "MIT", "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 20.19.4" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { - "@types/react": "^19.1.0", - "react": "*", - "react-native": "*" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@react-navigation/bottom-tabs": { - "version": "7.15.9", - "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.15.9.tgz", - "integrity": "sha512-Ou28A1aZLj5wiFQ3F93aIsrI4NCwn3IJzkkjNo9KLFXsc0Yks+UqrVaFlffHFLsrbajuGRG/OQpnMA1ljayY5Q==", + "node_modules/@radix-ui/react-dialog/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": { - "@react-navigation/elements": "^2.9.14", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-navigation/bottom-tabs/node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "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", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "engines": { - "node": ">=12.5.0" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-navigation/bottom-tabs/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==", + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-navigation/bottom-tabs/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==", - "license": "MIT" + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@react-navigation/core": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.17.2.tgz", - "integrity": "sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA==", + "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": { - "@react-navigation/routers": "^7.5.3", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "query-string": "^7.1.3", - "react-is": "^19.1.0", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { - "react": ">= 18.2.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-navigation/elements": { - "version": "2.9.14", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.14.tgz", - "integrity": "sha512-lKqzu+su2pI/YIZmR7L7xdOs4UL+rVXKJAMpRMBrwInEy96SjIFst6QDGpE89Dunnu3VjVpjWfByo9f2GWBHDQ==", + "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": { - "color": "^4.2.3", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { - "@react-native-masked-view/masked-view": ">= 0.2.0", - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "@react-native-masked-view/masked-view": { + "@types/react": { "optional": true } } }, - "node_modules/@react-navigation/elements/node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "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": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": ">=12.5.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-navigation/elements/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==", + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-navigation/elements/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==", - "license": "MIT" - }, - "node_modules/@react-navigation/native": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.2.2.tgz", - "integrity": "sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w==", + "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": { - "@react-navigation/core": "^7.17.2", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "use-latest-callback": "^0.2.4" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { - "react": ">= 18.2.0", - "react-native": "*" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-navigation/routers": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", - "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", + "node_modules/@radix-ui/react-primitive/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": { - "nanoid": "^3.3.11" + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", - "cpu": [ - "x64" - ], - "dev": true, + "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", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", - "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", - "cpu": [ - "arm" - ], - "dev": true, + "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", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", - "cpu": [ - "arm64" - ], - "dev": true, + "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", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", - "cpu": [ - "arm64" - ], - "dev": true, + "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", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", - "cpu": [ - "ppc64" - ], - "dev": true, + "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", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@react-native/assets-registry": { + "version": "0.83.6", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 20.19.4" } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@react-native/babel-plugin-codegen": { + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.83.6.tgz", + "integrity": "sha512-qfRXsHGeucT5c6mK+8Q7v4Ly3zmygfVmFlEtkiq7q07W1OTreld6nib4rJ/DBEeNiKBoBTuHjWliYGNuDjLFQA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/traverse": "^7.25.3", + "@react-native/codegen": "0.83.6" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 20.19.4" } }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@react-native/babel-preset": { + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.83.6.tgz", + "integrity": "sha512-4/fXFDUvGOObETZq4+SUFkafld6OGgQWut5cQiqVghlhCB5z/p2lVhPgEUr/aTxTzeS3AmN+ztC+GpYPQ7tsTw==", "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/plugin-transform-classes": "^7.25.4", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.25.2", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/template": "^7.25.0", + "@react-native/babel-plugin-codegen": "0.83.6", + "babel-plugin-syntax-hermes-parser": "0.32.0", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", - "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", - "cpu": [ - "wasm32" - ], - "dev": true, + "node_modules/@react-native/babel-preset/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", + "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "1.9.2", - "@emnapi/runtime": "1.9.2", - "@napi-rs/wasm-runtime": "^1.1.3" - }, - "engines": { - "node": ">=14.0.0" + "hermes-parser": "0.32.0" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@react-native/babel-preset/node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=0.10.0" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@react-native/codegen": { + "version": "0.83.6", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.83.6.tgz", + "integrity": "sha512-doB/Pq6Cf6IjF3wlQXTIiZOnsX9X8mEEk+CdGfyuCwZjWrf7IB8KaZEXXckJmfUcIwvJ9u/a72ZoTTCIoxAc9A==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.25.3", + "glob": "^7.1.1", + "hermes-parser": "0.32.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "yargs": "^17.6.2" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", - "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@simple-git/args-pathspec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.2.tgz", - "integrity": "sha512-nEFVejViHUoL8wU8GTcwqrvqfUG40S5ts6S4fr1u1Ki5CklXlRDYThPVA/qurTmCYFGnaX3XpVUmICLHdvhLaA==", - "license": "MIT" - }, - "node_modules/@simple-git/argv-parser": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.0.3.tgz", - "integrity": "sha512-NMKv9sJcSN2VvnPT9Ja7eKfGy8Q8mMFLwPTCcuZMtv3+mYcLIZflg31S/tp2XCCyiY7YAx6cgBHQ0fwA2fWHpQ==", + "node_modules/@react-native/community-cli-plugin": { + "version": "0.83.6", "license": "MIT", "dependencies": { - "@simple-git/args-pathspec": "^1.0.2" + "@react-native/dev-middleware": "0.83.6", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.83.6", + "metro-config": "^0.83.6", + "metro-core": "^0.83.6", + "semver": "^7.1.3" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "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==", + "node_modules/@react-native/debugger-frontend": { + "version": "0.83.6", "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" + "engines": { + "node": ">= 20.19.4" } }, - "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==", - "license": "BSD-3-Clause", + "node_modules/@react-native/debugger-shell": { + "version": "0.83.6", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "cross-spawn": "^7.0.6", + "fb-dotslash": "0.5.8" + }, + "engines": { + "node": ">= 20.19.4" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@testing-library/react-native": { - "version": "13.3.3", - "resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-13.3.3.tgz", - "integrity": "sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==", - "dev": true, + "node_modules/@react-native/dev-middleware": { + "version": "0.83.6", "license": "MIT", "dependencies": { - "jest-matcher-utils": "^30.0.5", - "picocolors": "^1.1.1", - "pretty-format": "^30.0.5", - "redent": "^3.0.0" + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.83.6", + "@react-native/debugger-shell": "0.83.6", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.2.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "serve-static": "^1.16.2", + "ws": "^7.5.10" }, "engines": { - "node": ">=18" + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/ws": { + "version": "7.5.10", + "license": "MIT", + "engines": { + "node": ">=8.3.0" }, "peerDependencies": { - "jest": ">=29.0.0", - "react": ">=18.2.0", - "react-native": ">=0.71", - "react-test-renderer": ">=18.2.0" + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" }, "peerDependenciesMeta": { - "jest": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { "optional": true } } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, + "node_modules/@react-native/gradle-plugin": { + "version": "0.83.6", "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "engines": { + "node": ">= 20.19.4" } }, - "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==", + "node_modules/@react-native/js-polyfills": { + "version": "0.83.6", "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "engines": { + "node": ">= 20.19.4" } }, - "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==", + "node_modules/@react-native/normalize-colors": { + "version": "0.83.6", + "license": "MIT" + }, + "node_modules/@react-native/virtualized-lists": { + "version": "0.83.6", "license": "MIT", "dependencies": { - "@babel/types": "^7.0.0" + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "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==", + "node_modules/@react-navigation/bottom-tabs": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.16.1.tgz", + "integrity": "sha512-wjFATJmbq0K8B96Ax0JcK2+Eu7syfYvQ5qUd/tgcv8JuCYLwKKqojJMAl31qdjpKqFG09pQ6TSdEDHOek60CAA==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "@react-navigation/elements": "^2.9.18", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0" + }, + "peerDependencies": { + "@react-navigation/native": "^7.2.4", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.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==", + "node_modules/@react-navigation/core": { + "version": "7.17.4", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.17.4.tgz", + "integrity": "sha512-Rv9E2oNNQEkPGpmu9q+vJwGJRSQR6LBg5L+Yo1QHjtwGbHUbjkIKOdYymDZoZYgNzX2OD4rAIlfuzbDKa3cCeA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "@react-navigation/routers": "^7.5.5", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "query-string": "^7.1.3", + "react-is": "^19.1.0", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "react": ">= 18.2.0" } }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, + "node_modules/@react-navigation/elements": { + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.18.tgz", + "integrity": "sha512-mKEvDr6CkCVYZSb8W9WubNseihL+1c8M7ktZJCTCbMk8rQgdQfkdRNwpSUQKspdGpUHCb9cyzvaiuzl1NtjVgw==", "license": "MIT", "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" + "color": "^4.2.3", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@react-native-masked-view/masked-view": ">= 0.2.0", + "@react-navigation/native": "^7.2.4", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0" + }, + "peerDependenciesMeta": { + "@react-native-masked-view/masked-view": { + "optional": true + } } }, - "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==", + "node_modules/@react-navigation/native": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.2.4.tgz", + "integrity": "sha512-eWC2D3JjhYLId2fVTZhhCiUpWIaPhO9XyEb7Wq8ElmOHyIODlbOzgZ0rKia02OIsDKr9BzZl2sK1dL70yMxDaw==", "license": "MIT", "dependencies": { - "@types/node": "*" + "@react-navigation/core": "^7.17.4", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "use-latest-callback": "^0.2.4" + }, + "peerDependencies": { + "react": ">= 18.2.0", + "react-native": "*" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "node_modules/@react-navigation/native-stack": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.15.1.tgz", + "integrity": "sha512-kNrJggwoB/onC0MpZIuZ6qaqeAziFchz+W9txBzhd6qbWmB1OkPVUnu6fWgc6BQc7MeMf59djVmqgX+6kJU1Ug==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.9.18", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0", + "warn-once": "^0.1.1" + }, + "peerDependencies": { + "@react-navigation/native": "^7.2.4", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, + "node_modules/@react-navigation/routers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.5.tgz", + "integrity": "sha512-9/hhMte12Kgu+pMnLfA4EWJ0OQmIEAMVMX06FPH2yGkEQSQ3JhhCN/GkcRikzQhtEi97VYYQA15umptBUShcOQ==", + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", "dev": true, "license": "MIT" }, - "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==", - "license": "MIT", + "node_modules/@segment/loosely-validate-event": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", + "integrity": "sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==", "dependencies": { - "@types/node": "*" + "component-type": "^1.2.1", + "join-component": "^1.1.0" } }, - "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==", + "node_modules/@sentry-internal/tracing": { + "version": "7.77.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.77.0.tgz", + "integrity": "sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw==", "license": "MIT", "dependencies": { - "@types/unist": "*" + "@sentry/core": "7.77.0", + "@sentry/types": "7.77.0", + "@sentry/utils": "7.77.0" + }, + "engines": { + "node": ">=8" } }, - "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==", - "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==", + "node_modules/@sentry/core": { + "version": "7.77.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.77.0.tgz", + "integrity": "sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg==", "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@sentry/types": "7.77.0", + "@sentry/utils": "7.77.0" + }, + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/@sentry/node": { + "version": "7.77.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.77.0.tgz", + "integrity": "sha512-Ob5tgaJOj0OYMwnocc6G/CDLWC7hXfVvKX/ofkF98+BbN/tQa5poL+OwgFn9BA8ud8xKzyGPxGU6LdZ8Oh3z/g==", "license": "MIT", "dependencies": { - "@types/istanbul-lib-report": "*" + "@sentry-internal/tracing": "7.77.0", + "@sentry/core": "7.77.0", + "@sentry/types": "7.77.0", + "@sentry/utils": "7.77.0", + "https-proxy-agent": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "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/@sentry/node/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", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } }, - "node_modules/@types/node": { - "version": "20.19.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", - "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "node_modules/@sentry/node/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", "dependencies": { - "undici-types": "~6.21.0" + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/@types/prismjs": { - "version": "1.26.6", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", - "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", - "license": "MIT" + "node_modules/@sentry/types": { + "version": "7.77.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.77.0.tgz", + "integrity": "sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/@types/react": { - "version": "19.0.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz", - "integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==", + "node_modules/@sentry/utils": { + "version": "7.77.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.77.0.tgz", + "integrity": "sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g==", "license": "MIT", "dependencies": { - "csstype": "^3.0.2" + "@sentry/types": "7.77.0" + }, + "engines": { + "node": ">=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", + "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": { - "@types/react": "*" + "@hapi/hoek": "^9.0.0" } }, - "node_modules/@types/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", - "dev": true, - "license": "MIT" + "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/@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==", - "license": "MIT" + "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/@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==", + "node_modules/@simple-git/args-pathspec": { + "version": "1.0.3", "license": "MIT" }, - "node_modules/@types/vscode": { - "version": "1.115.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.115.0.tgz", - "integrity": "sha512-/M8cdznOlqtMqduHKKlIF00v4eum4ZWKgn8YoPRKcN6PDdvoWeeqDaQSnw63ipDbq1Uzz78Wndk/d0uSPwORfA==", - "dev": true, + "node_modules/@simple-git/argv-parser": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@simple-git/args-pathspec": "^1.0.3" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", "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==", + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "license": "BSD-3-Clause", "dependencies": { - "@types/node": "*" + "type-detect": "4.0.8" } }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "license": "MIT", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "license": "BSD-3-Clause", "dependencies": { - "@types/yargs-parser": "*" + "@sinonjs/commons": "^3.0.0" } }, - "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==", + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "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, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "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", + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "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", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/cli-progress": { + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", + "integrity": "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/emscripten": { + "version": "1.41.5", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.41.5.tgz", + "integrity": "sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "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/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.19.41", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "license": "MIT" + }, + "node_modules/@types/vscode": { + "version": "1.115.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "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" }, @@ -4157,1759 +4816,2958 @@ } } }, - "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, + "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", + "license": "ISC" + }, + "node_modules/@urql/core": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-4.0.11.tgz", + "integrity": "sha512-FFdY97vF5xnUrElcGw9erOLvtu+KGMLfwrLNDfv4IPgdp2IBsiGe+Kb7Aypfd3kH//BETewVSLm3+y2sSzjX6A==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.1", + "wonka": "^6.3.2" + } + }, + "node_modules/@urql/exchange-retry": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.2.0.tgz", + "integrity": "sha512-1O/biKiVhhn0EtvDF4UOvz325K4RrLupfL8rHcmqD2TBLv4qVDWQuzx4JGa1FfqjjRb+C9TNZ6w19f32Mq85Ug==", + "license": "MIT", + "dependencies": { + "@urql/core": ">=4.0.0", + "wonka": "^6.3.2" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.4", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.4", + "vitest": "4.1.4" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.4", + "@vitest/utils": "4.1.4", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.4", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.4", + "@vitest/utils": "4.1.4", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.4", + "fflate": "^0.8.2", + "flatted": "^3.4.2", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.4" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.4", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "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/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/anser": { + "version": "1.4.10", + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "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", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "license": "MIT" + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "license": "Python-2.0" + }, + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "license": "MIT" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "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", + "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-jest-hoist": { + "version": "29.6.3", + "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-plugin-polyfill-corejs2": { + "version": "0.4.17", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-react-compiler": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + } + }, + "node_modules/babel-plugin-react-native-web": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", + "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", + "license": "MIT" + }, + "node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.1.tgz", + "integrity": "sha512-HgErPZTghW76Rkq9uqn5ESeiD97FbqpZ1V170T1RG2RDp+7pJVQV2pQJs7y5YzN0/gcT6GM5ci9apRnIwuyPdQ==", + "license": "MIT", + "dependencies": { + "hermes-parser": "0.32.1" + } + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-estree": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.1.tgz", + "integrity": "sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg==", + "license": "MIT" + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-parser": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.1.tgz", + "integrity": "sha512-175dz634X/W5AiwrpLdoMl/MOb17poLHyIqgyExlE8D9zQ1OPnoORnGMB5ltRKnpvQzBjMYvT2rN/sHeIfZW5Q==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.32.1" + } + }, + "node_modules/babel-plugin-transform-flow-enums": { + "version": "0.0.2", + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-flow": "^7.12.1" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "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-expo": { + "version": "55.0.21", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-55.0.21.tgz", + "integrity": "sha512-anXoUZBcxydLdVs2L+r3bWKGUvZv2FtgOl8xRJ12i/YfKICBpwTGZWSTiEYTqBByZ6GkA3mE9+3TW97X2ocFTQ==", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.20.5", + "@babel/helper-module-imports": "^7.25.9", + "@babel/plugin-proposal-decorators": "^7.12.9", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.0", + "@react-native/babel-preset": "0.83.6", + "babel-plugin-react-compiler": "^1.0.0", + "babel-plugin-react-native-web": "~0.21.0", + "babel-plugin-syntax-hermes-parser": "^0.32.0", + "babel-plugin-transform-flow-enums": "^0.0.2", + "debug": "^4.3.4", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "@babel/runtime": "^7.20.0", + "expo": "*", + "expo-widgets": "^55.0.17", + "react-refresh": ">=0.14.0 <1.0.0" + }, + "peerDependenciesMeta": { + "@babel/runtime": { + "optional": true + }, + "expo": { + "optional": true + }, + "expo-widgets": { + "optional": true + } + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "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", + "license": "MIT" + }, + "node_modules/barcode-detector": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/barcode-detector/-/barcode-detector-3.1.3.tgz", + "integrity": "sha512-omL3/x26oU9jlR0gUQcGdXIjQtMlrUGKF7xRFO1RwrQkRkRU7WLz0mgQEsdUtYBm2uX3JH+HQLrKlyTS/BxZRw==", + "license": "MIT", + "dependencies": { + "zxing-wasm": "3.0.3" + } + }, + "node_modules/bare-events": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.3.tgz", + "integrity": "sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "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/baseline-browser-mapping": { + "version": "2.10.30", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/better-opn": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "open": "^8.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/better-opn/node_modules/open": { + "version": "8.4.2", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-creator": { + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.1", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.3", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "engines": [ + "node >=0.10.0" + ], "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" + "bin": { + "bunyan": "bin/bunyan" }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" } }, - "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, + "node_modules/bytes": { + "version": "3.1.2", "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": ">= 0.8" } }, - "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/@urql/core": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.2.0.tgz", - "integrity": "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==", + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "license": "MIT", - "dependencies": { - "@0no-co/graphql.web": "^1.0.13", - "wonka": "^6.3.2" + "engines": { + "node": ">=10.6.0" } }, - "node_modules/@urql/exchange-retry": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.2.tgz", - "integrity": "sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg==", + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "license": "MIT", "dependencies": { - "@urql/core": "^5.1.2", - "wonka": "^6.3.2" + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" }, - "peerDependencies": { - "@urql/core": "^5.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/@vitest/coverage-v8": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.4.tgz", - "integrity": "sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w==", - "dev": 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": { - "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.4", - "ast-v8-to-istanbul": "^1.0.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.2.0", - "magicast": "^0.5.2", - "obug": "^2.1.1", - "std-env": "^4.0.0-rc.1", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "4.1.4", - "vitest": "4.1.4" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } + "engines": { + "node": ">= 0.4" } }, - "node_modules/@vitest/expect": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz", - "integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==", + "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", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.4", - "@vitest/utils": "4.1.4", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=6" } }, - "node_modules/@vitest/mocker": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz", - "integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==", - "dev": true, + "node_modules/camelcase": { + "version": "6.3.0", "license": "MIT", - "dependencies": { - "@vitest/spy": "4.1.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, - "vite": { - "optional": true + { + "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/@vitest/pretty-format": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", - "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", - "dev": true, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", "license": "MIT", "dependencies": { - "tinyrainbow": "^3.1.0" + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "bin": { + "cdl": "bin/cdl.js" } }, - "node_modules/@vitest/runner": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz", - "integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==", + "node_modules/chai": { + "version": "6.2.2", "dev": true, "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.4", - "pathe": "^2.0.3" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@vitest/snapshot": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz", - "integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==", - "dev": true, - "license": "MIT", + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "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" + } + }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "license": "Apache-2.0", "dependencies": { - "@vitest/pretty-format": "4.1.4", - "@vitest/utils": "4.1.4", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" } }, - "node_modules/@vitest/spy": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz", - "integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" + "node_modules/chromium-edge-launcher": { + "version": "0.2.0", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" } }, - "node_modules/@vitest/ui": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.4.tgz", - "integrity": "sha512-EgFR7nlj5iTDYZYCvavjFokNYwr3c3ry0sFiCg+N7B233Nwp+NNx7eoF/XvMWDCKY71xXAG3kFkt97ZHBJVL8A==", - "dev": true, + "node_modules/ci-info": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.4", - "fflate": "^0.8.2", - "flatted": "^3.4.2", - "pathe": "^2.0.3", - "sirv": "^3.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0" + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" }, - "peerDependencies": { - "vitest": "4.1.4" + "engines": { + "node": ">=4" } }, - "node_modules/@vitest/utils": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", - "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", - "dev": true, + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" + "string-width": "^4.2.3" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=4" } }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", - "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", + "node_modules/cli-spinners": { + "version": "2.9.2", "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "license": "ISC", "dependencies": { - "event-target-shim": "^5.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=6.5" + "node": ">=12" } }, - "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==", + "node_modules/clone": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mimic-response": "^1.0.0" }, - "engines": { - "node": ">= 0.6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "node_modules/codelink-extension": { + "resolved": "packages/vscode-extension", + "link": true + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=12.5.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, + "node_modules/color-convert": { + "version": "1.9.3", "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "node_modules/color-name": { + "version": "1.1.3", + "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==", "license": "MIT", - "engines": { - "node": ">= 14" + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, + "node_modules/color/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==", "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" + "color-name": "~1.1.4" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/anser": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", - "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "node_modules/color/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==", "license": "MIT" }, - "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==", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "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==", - "license": "(MIT OR CC0-1.0)", + "node_modules/commander": { + "version": "7.2.0", + "license": "MIT", "engines": { - "node": ">=10" - }, + "node": ">= 10" + } + }, + "node_modules/component-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.2.tgz", + "integrity": "sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA==", + "license": "MIT", "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==", + "node_modules/compressible": { + "version": "2.0.18", "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "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==", + "node_modules/compression": { + "version": "1.8.1", "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "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": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.8.0" } }, - "node_modules/ansi-styles/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==", + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "ms": "2.0.0" } }, - "node_modules/ansi-styles/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==", + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", "license": "MIT" }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", "license": "MIT" }, - "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", + "node_modules/connect": { + "version": "3.7.0", + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" }, "engines": { - "node": ">= 8" + "node": ">= 0.10.0" } }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "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": ">=8.6" + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "license": "MIT" }, - "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==", - "license": "Python-2.0" + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } }, - "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, + "node_modules/cross-spawn": { + "version": "7.0.6", "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/ast-v8-to-istanbul": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", - "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", - "dev": true, + "node_modules/css-in-js-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", + "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.31", - "estree-walker": "^3.0.3", - "js-tokens": "^10.0.0" + "hyphenate-style-name": "^1.0.3" } }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "node_modules/csstype": { + "version": "3.2.3", "dev": true, "license": "MIT" }, - "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==", + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.4.3", "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" + "ms": "^2.1.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0" }, - "peerDependencies": { - "@babel/core": "^7.8.0" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "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==", - "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" - }, + "node_modules/decamelize": { + "version": "1.2.0", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "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==", + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "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": ">=0.10" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", - "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "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": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.8", - "semver": "^6.3.1" + "mimic-response": "^3.1.0" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/decompress-response/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/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "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==", "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", - "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "node_modules/defaults": { + "version": "1.0.4", "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8" + "clone": "^1.0.2" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/babel-plugin-react-compiler": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", - "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.0" + "engines": { + "node": ">=10" } }, - "node_modules/babel-plugin-react-native-web": { - "version": "0.19.13", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz", - "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==", - "dev": true, - "license": "MIT" + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", - "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", - "dependencies": { - "hermes-parser": "0.29.1" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/babel-plugin-transform-flow-enums": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", - "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", + "node_modules/depd": { + "version": "2.0.0", "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" + "engines": { + "node": ">= 0.8" } }, - "node_modules/babel-preset-current-node-syntax": { + "node_modules/destroy": { "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==", "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" + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/babel-preset-expo": { - "version": "54.0.10", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-54.0.10.tgz", - "integrity": "sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.81.5", - "babel-plugin-react-compiler": "^1.0.0", - "babel-plugin-react-native-web": "~0.21.0", - "babel-plugin-syntax-hermes-parser": "^0.29.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "debug": "^4.3.4", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "@babel/runtime": "^7.20.0", - "expo": "*", - "react-refresh": ">=0.14.0 <1.0.0" - }, - "peerDependenciesMeta": { - "@babel/runtime": { - "optional": true - }, - "expo": { - "optional": true - } + "node_modules/detect-libc": { + "version": "2.1.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" } }, - "node_modules/babel-preset-expo/node_modules/babel-plugin-react-native-web": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", - "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", + "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/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==", - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "license": "BSD-3-Clause", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=0.3.1" } }, - "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==", - "license": "MIT" + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" }, - "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" - } - ], + "node_modules/dijkstrajs": { + "version": "1.0.3", "license": "MIT" }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "node_modules/dir-glob": { + "version": "3.0.1", "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, "engines": { - "node": "^4.5.0 || >= 5.9" + "node": ">=8" } }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.16", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", - "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", + "node_modules/dnssd-advertise": { + "version": "1.1.4", + "license": "MIT" + }, + "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", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" + "dependencies": { + "esutils": "^2.0.2" }, "engines": { "node": ">=6.0.0" } }, - "node_modules/better-opn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", - "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", - "license": "MIT", - "dependencies": { - "open": "^8.0.4" - }, + "node_modules/domino": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.7.tgz", + "integrity": "sha512-3rcXhx0ixJV2nj8J0tljzejTF73A35LVVdnTQu79UAqTBFEgYPMgGtykMuu/BDqaOZphATku1ddRUn/RtqUHYQ==", + "license": "BSD-2-Clause" + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "license": "BSD-2-Clause", "engines": { - "node": ">=12.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, - "node_modules/better-opn/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "license": "BSD-2-Clause", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "dotenv": "^16.4.5" }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://dotenvx.com" } }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "license": "Unlicense", + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", "engines": { - "node": ">=0.6" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/bplist-creator": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", - "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", - "license": "MIT", + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "hasInstallScript": true, + "license": "BSD-2-Clause", + "optional": true, "dependencies": { - "stream-buffers": "2.2.x" + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/bplist-parser": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", - "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "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": { - "big-integer": "1.6.x" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">= 5.10.0" + "node": ">= 0.4" } }, - "node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "node_modules/eas-cli": { + "version": "18.13.1", + "resolved": "https://registry.npmjs.org/eas-cli/-/eas-cli-18.13.1.tgz", + "integrity": "sha512-OlusTcdrjfpIKsieU7JXYKugXTBOklfPj56ySYTJiZBnIWV+EtwXHAL13h5RTu+I7qMAK2ZbcBYk6ig4TZWe5A==", + "license": "MIT", + "dependencies": { + "@expo/apple-utils": "2.1.19", + "@expo/code-signing-certificates": "0.0.5", + "@expo/config": "55.0.10", + "@expo/config-plugins": "55.0.7", + "@expo/eas-build-job": "18.13.1", + "@expo/eas-json": "18.13.1", + "@expo/env": "^1.0.0", + "@expo/json-file": "8.3.3", + "@expo/logger": "18.5.0", + "@expo/multipart-body-parser": "2.0.0", + "@expo/osascript": "2.1.4", + "@expo/package-manager": "1.9.10", + "@expo/pkcs12": "0.1.3", + "@expo/plist": "0.2.0", + "@expo/plugin-help": "5.1.23", + "@expo/plugin-warn-if-update-available": "2.5.1", + "@expo/prebuild-config": "8.0.17", + "@expo/results": "1.0.0", + "@expo/rudder-sdk-node": "1.1.1", + "@expo/spawn-async": "1.7.2", + "@expo/steps": "18.13.1", + "@expo/timeago.js": "1.0.0", + "@oclif/core": "^4.8.3", + "@oclif/plugin-autocomplete": "^3.2.40", + "@segment/ajv-human-errors": "^2.1.2", + "@sentry/node": "7.77.0", + "@urql/core": "4.0.11", + "@urql/exchange-retry": "1.2.0", + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "better-opn": "3.0.2", + "bplist-parser": "^0.3.0", + "chalk": "4.1.2", + "cli-progress": "3.12.0", + "dateformat": "4.6.3", + "debug": "4.4.3", + "diff": "7.0.0", + "dotenv": "16.3.1", + "env-paths": "2.2.0", + "envinfo": "7.11.0", + "fast-deep-equal": "3.1.3", + "fast-glob": "3.3.2", + "figures": "3.2.0", + "form-data": "^4.0.4", + "fs-extra": "11.2.0", + "getenv": "1.0.0", + "gradle-to-js": "2.0.1", + "graphql": "16.8.1", + "graphql-tag": "2.12.6", + "https-proxy-agent": "5.0.1", + "ignore": "5.3.0", + "indent-string": "4.0.0", + "invariant": "^2.2.2", + "jks-js": "1.1.0", + "joi": "17.11.0", + "keychain": "1.5.0", + "log-symbols": "4.1.0", + "mime": "3.0.0", + "minimatch": "5.1.2", + "minizlib": "3.0.1", + "nanoid": "3.3.8", + "node-fetch": "2.6.7", + "node-forge": "1.3.1", + "node-stream-zip": "1.15.0", + "nullthrows": "1.1.1", + "ora": "5.1.0", + "pkg-dir": "4.2.0", + "pngjs": "7.0.0", + "promise-limit": "2.7.0", + "promise-retry": "2.0.1", + "prompts": "2.4.2", + "qrcode-terminal": "0.12.0", + "resolve-from": "5.0.0", + "semver": "7.5.4", + "set-interval-async": "3.0.3", + "slash": "3.0.0", + "tar": "7.5.7", + "tar-stream": "3.1.7", + "terminal-link": "2.1.1", + "ts-deepmerge": "6.2.0", + "tslib": "2.6.2", + "turndown": "7.1.2", + "untildify": "4.0.0", + "uuid": "9.0.1", + "wrap-ansi": "7.0.0", + "yaml": "2.6.0", + "zod": "^4.1.3" + }, + "bin": { + "eas": "bin/run" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/code-signing-certificates": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz", + "integrity": "sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "node-forge": "^1.2.1", + "nullthrows": "^1.1.1" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/eas-cli/node_modules/@expo/config": { + "version": "55.0.10", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.10.tgz", + "integrity": "sha512-qCHxo9H1ZoeW+y0QeMtVZ3JfGmumpGrgUFX60wLWMarraoQZSe47ZUm9kJSn3iyoPjUtUNanO3eXQg+K8k4rag==", "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "@expo/config-plugins": "~55.0.7", + "@expo/config-types": "^55.0.5", + "@expo/json-file": "^10.0.12", + "@expo/require-utils": "^55.0.3", + "deepmerge": "^4.3.1", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "resolve-from": "^5.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4" } }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "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" - } - ], + "node_modules/eas-cli/node_modules/@expo/config-plugins": { + "version": "55.0.7", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.7.tgz", + "integrity": "sha512-XZUoDWrsHEkH3yasnDSJABM/UxP5a1ixzRwU/M+BToyn/f0nTrSJJe/Ay/FpxkI4JSNz2n0e06I23b2bleXKVA==", "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, + "@expo/config-types": "^55.0.5", + "@expo/json-file": "~10.0.12", + "@expo/plist": "^0.5.2", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/config-plugins/node_modules/@expo/json-file": { + "version": "10.0.14", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.14.tgz", + "integrity": "sha512-yWwBFywFv+SxkJp/pIzzA416JVYflNUh7pqQzgaA6nXDqRyK7KfrqVzk8PdUfDnqbBcaZZxpzNssfQZzp5KHrA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" + } + }, + "node_modules/eas-cli/node_modules/@expo/config-plugins/node_modules/@expo/plist": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.3.tgz", + "integrity": "sha512-jz5oPcPDd3fygwVxwSwmO6wodTwm0Qa14NUyPy0ka7H8sFmCtNZUI2+DzVe/EXjOhq1FbEjrwl89gdlWYOnVjQ==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + } + }, + "node_modules/eas-cli/node_modules/@expo/config-plugins/node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", + "license": "MIT", "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "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==", - "license": "Apache-2.0", + "node_modules/eas-cli/node_modules/@expo/config/node_modules/@expo/json-file": { + "version": "10.0.14", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.14.tgz", + "integrity": "sha512-yWwBFywFv+SxkJp/pIzzA416JVYflNUh7pqQzgaA6nXDqRyK7KfrqVzk8PdUfDnqbBcaZZxpzNssfQZzp5KHrA==", + "license": "MIT", "dependencies": { - "node-int64": "^0.4.0" + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" } }, - "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" - } - ], + "node_modules/eas-cli/node_modules/@expo/config/node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eas-cli/node_modules/@expo/config/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eas-cli/node_modules/@expo/env": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-1.0.7.tgz", + "integrity": "sha512-qSTEnwvuYJ3umapO9XJtrb1fAqiPlmUUg78N0IZXXGwQRt+bkp0OBls+Y5Mxw/Owj8waAM0Z3huKKskRADR5ow==", "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "chalk": "^4.0.0", + "debug": "^4.3.4", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "getenv": "^2.0.0" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" + "node_modules/eas-cli/node_modules/@expo/env/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/eas-cli/node_modules/@expo/env/node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "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==", - "dev": true, + "node_modules/eas-cli/node_modules/@expo/image-utils": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.6.5.tgz", + "integrity": "sha512-RsS/1CwJYzccvlprYktD42KjyfWZECH6PPIEowvoSmXfGLfdViwcUEI4RvBfKX5Jli6P67H+6YmHvPTbGOboew==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.0.0", + "fs-extra": "9.0.0", + "getenv": "^1.0.0", + "jimp-compact": "0.16.1", + "parse-png": "^2.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "temp-dir": "~2.0.0", + "unique-string": "~2.0.0" } }, - "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, + "node_modules/eas-cli/node_modules/@expo/image-utils/node_modules/fs-extra": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", + "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", + "node_modules/eas-cli/node_modules/@expo/image-utils/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001787", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", - "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", - "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/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", - "dev": true, + "node_modules/eas-cli/node_modules/@expo/image-utils/node_modules/universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 10.0.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==", + "node_modules/eas-cli/node_modules/@expo/json-file": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.3.3.tgz", + "integrity": "sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A==", "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" + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.2", + "write-file-atomic": "^2.3.0" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "node_modules/eas-cli/node_modules/@expo/json-file/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "@babel/highlight": "^7.10.4" } }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "node_modules/eas-cli/node_modules/@expo/osascript": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.4.tgz", + "integrity": "sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "exec-async": "^2.2.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "node_modules/eas-cli/node_modules/@expo/package-manager": { + "version": "1.9.10", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.9.10.tgz", + "integrity": "sha512-axJm+NOj3jVxep49va/+L3KkF3YW/dkV+RwzqUJedZrv4LeTqOG4rhrCaCPXHTvLqCTDKu6j0Xyd28N7mnxsGA==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "@expo/json-file": "^10.0.8", + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.0.0", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "resolve-workspace-root": "^2.0.0" } }, - "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" + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/@expo/json-file": { + "version": "10.0.14", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.14.tgz", + "integrity": "sha512-yWwBFywFv+SxkJp/pIzzA416JVYflNUh7pqQzgaA6nXDqRyK7KfrqVzk8PdUfDnqbBcaZZxpzNssfQZzp5KHrA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" } }, - "node_modules/chrome-launcher": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", - "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", - "license": "Apache-2.0", + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "license": "MIT", "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0" - }, - "bin": { - "print-chrome-path": "bin/print-chrome-path.js" + "chalk": "^2.0.1" }, "engines": { - "node": ">=12.13.0" + "node": ">=4" } }, - "node_modules/chromium-edge-launcher": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", - "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", - "license": "Apache-2.0", + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/ora/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "license": "MIT", "dependencies": { - "restore-cursor": "^2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "node_modules/eas-cli/node_modules/@expo/package-manager/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, "engines": { "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "license": "ISC", + "node_modules/eas-cli/node_modules/@expo/plist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.2.0.tgz", + "integrity": "sha512-F/IZJQaf8OIVnVA6XWUeMPC3OH6MV00Wxf0WC0JhTQht2QgjyHUa3U5Gs3vRtDq8tXNsZneOQRDVwpaOnd4zTQ==", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "@xmldom/xmldom": "~0.7.7", + "base64-js": "^1.2.3", + "xmlbuilder": "^14.0.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/plist/node_modules/@xmldom/xmldom": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", + "integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==", + "deprecated": "this version has critical issues, please update to the latest version", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10.0.0" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "node_modules/eas-cli/node_modules/@expo/plist/node_modules/xmlbuilder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", + "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", "license": "MIT", "engines": { - "node": ">=0.8" + "node": ">=8.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config": { + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-8.0.17.tgz", + "integrity": "sha512-HM+XpDox3fAZuXZXvy55VRcBbsZSDijGf8jI8i/pexgWvtsnt1ouelPXRuE1pXDicMX+lZO83QV+XkyLmBEXYQ==", + "license": "MIT", + "dependencies": { + "@expo/config": "~10.0.4", + "@expo/config-plugins": "~9.0.0", + "@expo/config-types": "^52.0.0", + "@expo/image-utils": "^0.6.0", + "@expo/json-file": "^9.0.0", + "@react-native/normalize-colors": "0.76.2", + "debug": "^4.3.1", + "fs-extra": "^9.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "xml2js": "0.6.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@expo/config": { + "version": "10.0.11", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-10.0.11.tgz", + "integrity": "sha512-nociJ4zr/NmbVfMNe9j/+zRlt7wz/siISu7PjdWE4WE+elEGxWWxsGzltdJG0llzrM+khx8qUiFK5aiVcdMBww==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "@expo/config-plugins": "~9.0.17", + "@expo/config-types": "^52.0.5", + "@expo/json-file": "^9.0.2", + "deepmerge": "^4.3.1", + "getenv": "^1.0.0", + "glob": "^10.4.2", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4", + "sucrase": "3.35.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@expo/config-plugins": { + "version": "9.0.17", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.17.tgz", + "integrity": "sha512-m24F1COquwOm7PBl5wRbkT9P9DviCXe0D7S7nQsolfbhdCWuvMkfXeoWmgjtdhy7sDlOyIgBrAdnB6MfsWKqIg==", + "license": "MIT", + "dependencies": { + "@expo/config-types": "^52.0.5", + "@expo/json-file": "~9.0.2", + "@expo/plist": "^0.2.2", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^1.0.0", + "glob": "^10.4.2", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@expo/config-plugins/node_modules/@expo/json-file": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.2.tgz", + "integrity": "sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3", + "write-file-atomic": "^2.3.0" } }, - "node_modules/codelink-extension": { - "resolved": "packages/vscode-extension", - "link": true + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@expo/config-types": { + "version": "52.0.5", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.5.tgz", + "integrity": "sha512-AMDeuDLHXXqd8W+0zSjIt7f37vUd/BP8p43k68NHpyAvQO+z8mbQZm3cNQVAMySeayK2XoPigAFB1JF2NFajaA==", + "license": "MIT" }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@expo/json-file": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.1.5.tgz", + "integrity": "sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA==", "license": "MIT", "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@expo/plist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.2.2.tgz", + "integrity": "sha512-ZZGvTO6vEWq02UAPs3LIdja+HRO18+LRI5QuDl6Hs3Ps7KX7xU6Y6kjahWKY37Rx2YjNpX07dGpBFzzC+vKa2g==", "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "@xmldom/xmldom": "~0.7.7", + "base64-js": "^1.2.3", + "xmlbuilder": "^14.0.0" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "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==", + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/@xmldom/xmldom": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", + "integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==", + "deprecated": "this version has critical issues, please update to the latest version", "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "engines": { + "node": ">=10.0.0" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "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", + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, "engines": { - "node": ">= 10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", "dependencies": { - "mime-db": ">= 1.43.0 < 2" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "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" + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" } }, - "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==", + "node_modules/eas-cli/node_modules/@expo/prebuild-config/node_modules/xmlbuilder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", + "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">=8.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==", + "node_modules/eas-cli/node_modules/@react-native/normalize-colors": { + "version": "0.76.2", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.2.tgz", + "integrity": "sha512-ICoOpaTLPsFQjNLSM00NgQr6wal300cZZonHVSDXKntX+BfkLeuCHRtr/Mn+klTtW+/1v2/2FRm9dXjvyGf9Dw==", "license": "MIT" }, - "node_modules/compression/node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "node_modules/eas-cli/node_modules/@segment/ajv-human-errors": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@segment/ajv-human-errors/-/ajv-human-errors-2.16.0.tgz", + "integrity": "sha512-cHNfZcbHrmuYOA7/Sn7HlIDHanamiRTZtngfxcAuFaKQjP7cSqsVHjLz38FI2FQ8JDLz3syGLaz10Gn2ddo7+w==", "license": "MIT", - "engines": { - "node": ">= 0.6" + "peerDependencies": { + "ajv": "^8.0.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "node_modules/eas-cli/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", "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" + "debug": "4" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 6.0.0" } }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/eas-cli/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "license": "MIT", "dependencies": { - "ms": "2.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/connect/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/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==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "node_modules/eas-cli/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=6" } }, - "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "node_modules/eas-cli/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "license": "MIT", "dependencies": { - "browserslist": "^4.28.1" + "color-convert": "^1.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "engines": { + "node": ">=4" } }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "node_modules/eas-cli/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": "18 || 20 || >=22" } }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "node_modules/eas-cli/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" + "engines": { + "node": ">=0.8.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==", + "node_modules/eas-cli/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">= 8" + "node": ">=8.6.0" } }, - "node_modules/css-in-js-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", - "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", + "node_modules/eas-cli/node_modules/getenv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz", + "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==", "license": "MIT", - "dependencies": { - "hyphenate-style-name": "^1.0.3" + "engines": { + "node": ">=6" } }, - "node_modules/cssstyle": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", - "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", - "dev": true, - "license": "MIT", + "node_modules/eas-cli/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cssstyle/node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", + "node_modules/eas-cli/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": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=18" + "node": ">= 6" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/eas-cli/node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": "18 || 20 || >=22" } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "dev": true, - "license": "MIT" - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", + "node_modules/eas-cli/node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", "dependencies": { - "character-entities": "^2.0.0" + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/eas-cli/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=4" } }, - "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==", + "node_modules/eas-cli/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", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, "engines": { - "node": ">=4.0.0" + "node": ">= 6" } }, - "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": "3.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", - "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==", + "node_modules/eas-cli/node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "node_modules/eas-cli/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/eas-cli/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "license": "MIT", "dependencies": { - "clone": "^1.0.2" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", + "node_modules/eas-cli/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", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, + "node_modules/eas-cli/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "license": "MIT", + "bin": { + "mime": "cli.js" + }, "engines": { - "node": ">=0.4.0" + "node": ">=10.0.0" } }, - "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==", + "node_modules/eas-cli/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==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", + "node_modules/eas-cli/node_modules/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=10" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", + "node_modules/eas-cli/node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "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, + "node_modules/eas-cli/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "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" - }, + "node_modules/eas-cli/node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { - "node": ">=6.0.0" + "node": ">= 6.13.0" } }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", + "node_modules/eas-cli/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, "engines": { - "node": ">=12" + "node": ">=6" }, "funding": { - "url": "https://dotenvx.com" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dotenv-expand": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", - "license": "BSD-2-Clause", + "node_modules/eas-cli/node_modules/ora": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", + "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", + "license": "MIT", "dependencies": { - "dotenv": "^16.4.5" + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.4.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "url": "https://dotenvx.com" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "dev": true, + "node_modules/eas-cli/node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "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.334", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz", - "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==", - "license": "ISC" - }, - "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==", - "license": "MIT" - }, - "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==", + "node_modules/eas-cli/node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=14.19.0" } }, - "node_modules/engine.io": { - "version": "6.6.6", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.6.tgz", - "integrity": "sha512-U2SN0w3OpjFRVlrc17E6TMDmH58Xl9rai1MblNjAdwWp07Kk+llmzX0hjDpQdrDGzwmvOtgM5yI+meYX6iZ2xA==", + "node_modules/eas-cli/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "license": "MIT", "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "@types/ws": "^8.5.12", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.18.3" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">=10.2.0" + "node": ">=8" } }, - "node_modules/engine.io-client": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz", - "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==", - "license": "MIT", + "node_modules/eas-cli/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "license": "ISC", "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.18.3", - "xmlhttprequest-ssl": "~2.1.1" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "node_modules/eas-cli/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "dependencies": { + "has-flag": "^3.0.0" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "engines": { + "node": ">=4" } }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "node_modules/eas-cli/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" + }, + "node_modules/eas-cli/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/eas-cli/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/eas-cli/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/eas-cli/node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">=10.0.0" + "node": ">= 14" } }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "node_modules/eas-cli/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", - "engines": { - "node": ">=10.0.0" + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "bin": { + "ejs": "bin/cli.js" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.357", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", + "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.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "license": "MIT", "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node": ">=6" } }, - "node_modules/env-editor": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz", - "integrity": "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==", + "node_modules/env-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/env-string/-/env-string-1.0.1.tgz", + "integrity": "sha512-/DhCJDf5DSFK32joQiWRpWrT0h7p3hVQfMKxiBb7Nt8C8IF8BYyPtclDnuGGLOoj16d/8udKeiE7JbkotDmorQ==", + "license": "MIT" + }, + "node_modules/envinfo": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", + "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, "engines": { - "node": ">=8" + "node": ">=4" + } + }, + "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" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" } }, + "node_modules/error-ex/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==", + "license": "MIT" + }, "node_modules/error-stack-parser": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", "license": "MIT", "dependencies": { "stackframe": "^1.3.4" @@ -5919,7 +7777,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5927,9 +7784,6 @@ }, "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==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5937,8 +7791,6 @@ }, "node_modules/es-module-lexer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, @@ -5946,7 +7798,6 @@ "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==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5959,7 +7810,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5973,8 +7823,6 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -5982,14 +7830,10 @@ }, "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==", "license": "MIT", "engines": { "node": ">=10" @@ -6086,9 +7930,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -6129,8 +7973,6 @@ }, "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==", "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -6178,8 +8020,6 @@ }, "node_modules/estree-walker": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { @@ -6188,8 +8028,6 @@ }, "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": { @@ -6198,8 +8036,6 @@ }, "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" @@ -6207,17 +8043,28 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/exec-async": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", + "integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==", + "license": "MIT" + }, "node_modules/expect-type": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -6225,32 +8072,32 @@ } }, "node_modules/expo": { - "version": "54.0.33", - "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz", - "integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==", + "version": "55.0.24", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "54.0.23", - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/devtools": "0.1.8", - "@expo/fingerprint": "0.15.4", - "@expo/metro": "~54.2.0", - "@expo/metro-config": "54.0.14", - "@expo/vector-icons": "^15.0.3", + "@expo/cli": "55.0.30", + "@expo/config": "~55.0.17", + "@expo/config-plugins": "~55.0.9", + "@expo/devtools": "55.0.3", + "@expo/fingerprint": "0.16.7", + "@expo/local-build-cache-provider": "55.0.13", + "@expo/log-box": "55.0.12", + "@expo/metro": "~55.1.1", + "@expo/metro-config": "55.0.21", + "@expo/vector-icons": "^15.0.2", "@ungap/structured-clone": "^1.3.0", - "babel-preset-expo": "~54.0.10", - "expo-asset": "~12.0.12", - "expo-constants": "~18.0.13", - "expo-file-system": "~19.0.21", - "expo-font": "~14.0.11", - "expo-keep-awake": "~15.0.8", - "expo-modules-autolinking": "3.0.24", - "expo-modules-core": "3.0.29", + "babel-preset-expo": "~55.0.21", + "expo-asset": "~55.0.17", + "expo-constants": "~55.0.16", + "expo-file-system": "~55.0.20", + "expo-font": "~55.0.7", + "expo-keep-awake": "~55.0.8", + "expo-modules-autolinking": "55.0.22", + "expo-modules-core": "55.0.25", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", - "whatwg-url-without-unicode": "8.0.0-3" + "whatwg-url-minimum": "^0.1.2" }, "bin": { "expo": "bin/cli", @@ -6276,40 +8123,33 @@ } } }, - "node_modules/expo-asset": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.12.tgz", - "integrity": "sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ==", + "node_modules/expo-camera": { + "version": "55.0.18", + "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-55.0.18.tgz", + "integrity": "sha512-Us/7JV6O1lHpLBGKJnK2s8gzmPcmMVJSV5586DBeO7x7AXzmvvVGtH+0nJRVIBE3MNzGzGWyfgievjr8QlE7dA==", "license": "MIT", "dependencies": { - "@expo/image-utils": "^0.8.8", - "expo-constants": "~18.0.12" + "barcode-detector": "^3.0.0" }, "peerDependencies": { "expo": "*", "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-blur": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-15.0.8.tgz", - "integrity": "sha512-rWyE1NBRZEu9WD+X+5l7gyPRszw7n12cW3IRNAb5i6KFzaBp8cxqT5oeaphJapqURvcqhkOZn2k5EtBSbsuU7w==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" + "react-native": "*", + "react-native-web": "*" + }, + "peerDependenciesMeta": { + "react-native-web": { + "optional": true + } } }, "node_modules/expo-constants": { - "version": "18.0.13", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.13.tgz", - "integrity": "sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==", + "version": "55.0.16", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-55.0.16.tgz", + "integrity": "sha512-Z15/No94UHoogD+pulxjudGAeOHTEIWZgb/vnX48Wx5D+apWTeCbnKxQZZtGQlosvduYL5kaic2/W8U+NHfBQQ==", "license": "MIT", "dependencies": { - "@expo/config": "~12.0.13", - "@expo/env": "~2.0.8" + "@expo/env": "~2.1.2" }, "peerDependencies": { "expo": "*", @@ -6317,9 +8157,7 @@ } }, "node_modules/expo-file-system": { - "version": "19.0.21", - "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.21.tgz", - "integrity": "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==", + "version": "55.0.20", "license": "MIT", "peerDependencies": { "expo": "*", @@ -6327,11 +8165,8 @@ } }, "node_modules/expo-font": { - "version": "55.0.6", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-55.0.6.tgz", - "integrity": "sha512-x9czUA3UQWjIwa0ZUEs/eWJNqB4mAue/m4ltESlNPLZhHL0nWWqIfsyHmklTLFH7mVfcHSJvew6k+pR2FE1zVw==", + "version": "55.0.7", "license": "MIT", - "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -6341,202 +8176,372 @@ "react-native": "*" } }, - "node_modules/expo-haptics": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-15.0.8.tgz", - "integrity": "sha512-lftutojy8Qs8zaDzzjwM3gKHFZ8bOOEZDCkmh2Ddpe95Ra6kt2izeOfOfKuP/QEh0MZ1j9TfqippyHdRd1ZM9g==", + "node_modules/expo-glass-effect": { + "version": "55.0.11", + "resolved": "https://registry.npmjs.org/expo-glass-effect/-/expo-glass-effect-55.0.11.tgz", + "integrity": "sha512-wqq7GUOqSkfoFJzreZvBG0jzjsq5c582m3glhWSjcmIuByxXXWp6j6GY6hyFuYKzpOXhbuvusVxGCQi0yWnp3g==", "license": "MIT", "peerDependencies": { - "expo": "*" + "expo": "*", + "react": "*", + "react-native": "*" } }, - "node_modules/expo-keep-awake": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz", - "integrity": "sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==", + "node_modules/expo-image": { + "version": "55.0.10", + "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-55.0.10.tgz", + "integrity": "sha512-We+vq/Z8jy8zmGxcOP8vrhiWkkwyXFdSks8cSlPi0bpu6D0Ei6l9Nj2xHWCD+yoENh92aCEe1+QRujAwXbogGA==", "license": "MIT", + "dependencies": { + "sf-symbols-typescript": "^2.2.0" + }, "peerDependencies": { "expo": "*", - "react": "*" + "react": "*", + "react-native": "*", + "react-native-web": "*" + }, + "peerDependenciesMeta": { + "react-native-web": { + "optional": true + } } }, - "node_modules/expo-linear-gradient": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-13.0.2.tgz", - "integrity": "sha512-EDcILUjRKu4P1rtWcwciN6CSyGtH7Bq4ll3oTRV7h3h8oSzSilH1g6z7kTAMlacPBKvMnkkWOGzW6KtgMKEiTg==", + "node_modules/expo-keep-awake": { + "version": "55.0.8", "license": "MIT", "peerDependencies": { - "expo": "*" + "expo": "*", + "react": "*" } }, "node_modules/expo-modules-autolinking": { - "version": "3.0.24", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.24.tgz", - "integrity": "sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ==", + "version": "55.0.22", "license": "MIT", "dependencies": { + "@expo/require-utils": "^55.0.5", "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", - "commander": "^7.2.0", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0" + "commander": "^7.2.0" }, "bin": { "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, - "node_modules/expo-modules-core": { - "version": "3.0.29", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.29.tgz", - "integrity": "sha512-LzipcjGqk8gvkrOUf7O2mejNWugPkf3lmd9GkqL9WuNyeN2fRwU0Dn77e3ZUKI3k6sI+DNwjkq4Nu9fNN9WS7Q==", + "node_modules/expo-router": { + "version": "55.0.14", + "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-55.0.14.tgz", + "integrity": "sha512-rOn/wosp2hAPM+O2o41hnarbP5Zqv9UkHWa31KoSoiOme1tpmZd2yc93XtRAtzP0P5E5xzqq7a2rbEAarpP5XA==", "license": "MIT", "dependencies": { - "invariant": "^2.2.4" + "@expo/metro-runtime": "^55.0.11", + "@expo/schema-utils": "^55.0.4", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-tabs": "^1.1.12", + "@react-navigation/bottom-tabs": "^7.15.5", + "@react-navigation/native": "^7.1.33", + "@react-navigation/native-stack": "^7.14.5", + "client-only": "^0.0.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "expo-glass-effect": "^55.0.11", + "expo-image": "^55.0.10", + "expo-server": "^55.0.9", + "expo-symbols": "^55.0.8", + "fast-deep-equal": "^3.1.3", + "invariant": "^2.2.4", + "nanoid": "^3.3.8", + "query-string": "^7.1.3", + "react-fast-compare": "^3.2.2", + "react-native-is-edge-to-edge": "^1.2.1", + "semver": "~7.6.3", + "server-only": "^0.0.1", + "sf-symbols-typescript": "^2.1.0", + "shallowequal": "^1.1.0", + "use-latest-callback": "^0.2.1", + "vaul": "^1.1.2" + }, + "peerDependencies": { + "@expo/log-box": "55.0.12", + "@expo/metro-runtime": "^55.0.11", + "@react-navigation/drawer": "^7.9.4", + "@testing-library/react-native": ">= 13.2.0", + "expo": "*", + "expo-constants": "^55.0.16", + "expo-linking": "^55.0.15", + "react": "*", + "react-dom": "*", + "react-native": "*", + "react-native-gesture-handler": "*", + "react-native-reanimated": "*", + "react-native-safe-area-context": ">= 5.4.0", + "react-native-screens": "*", + "react-native-web": "*", + "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" + }, + "peerDependenciesMeta": { + "@react-navigation/drawer": { + "optional": true + }, + "@testing-library/react-native": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native-gesture-handler": { + "optional": true + }, + "react-native-reanimated": { + "optional": true + }, + "react-native-web": { + "optional": true + }, + "react-server-dom-webpack": { + "optional": true + } + } + }, + "node_modules/expo-router/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, + "engines": { + "node": ">=10" + } + }, + "node_modules/expo-secure-store": { + "version": "55.0.14", + "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-55.0.14.tgz", + "integrity": "sha512-OKp9pDiTa4kgChop8+pTRJGBPhkJUcAxP5c6JbivNr4bmx3I+gKmAj1ov4KOXkY95TpWdHO+GQ4+0BgSY2P3JQ==", + "license": "MIT", "peerDependencies": { - "react": "*", - "react-native": "*" + "expo": "*" } }, "node_modules/expo-server": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.5.tgz", - "integrity": "sha512-IGR++flYH70rhLyeXF0Phle56/k4cee87WeQ4mamS+MkVAVP+dDlOHf2nN06Z9Y2KhU0Gp1k+y61KkghF7HdhA==", + "version": "55.0.9", "license": "MIT", "engines": { "node": ">=20.16.0" } }, - "node_modules/expo-splash-screen": { - "version": "55.0.16", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-55.0.16.tgz", - "integrity": "sha512-k0GuXde9QNB7AVwwpJ/BVnSOliLDJy9qC0k7QlOSdbLH4HR1YhX1kWVhL6Yq68vZ7qpHl5WQZv1DDk3SxOaExg==", + "node_modules/expo-status-bar": { + "version": "55.0.6", "license": "MIT", "dependencies": { - "@expo/prebuild-config": "^55.0.13" + "react-native-is-edge-to-edge": "^1.2.1" }, "peerDependencies": { - "expo": "*" + "react": "*", + "react-native": "*" } }, - "node_modules/expo-splash-screen/node_modules/@expo/config": { - "version": "55.0.13", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.13.tgz", - "integrity": "sha512-mO6le0JXEk7whsIb5E7rT36wOtdcLRFlApc7eLCOyu24uQUvWKk00HSEPVjiOuMd7EgYz/8JBPCA+Rb96uNjIg==", + "node_modules/expo-symbols": { + "version": "55.0.8", + "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-55.0.8.tgz", + "integrity": "sha512-Dg6BTu+fCWukdlh+3XYIr6NbqJWmK4aAQ6i6BInKnWU0ALuzVUJcMDq8Lk9bHok2hOh3OhzJqlCqEoBXPInIVQ==", "license": "MIT", "dependencies": { - "@expo/config-plugins": "~55.0.8", - "@expo/config-types": "^55.0.5", - "@expo/json-file": "^10.0.13", - "@expo/require-utils": "^55.0.3", - "deepmerge": "^4.3.1", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "resolve-from": "^5.0.0", - "resolve-workspace-root": "^2.0.0", - "semver": "^7.6.0", - "slugify": "^1.3.4" + "@expo-google-fonts/material-symbols": "^0.4.1", + "sf-symbols-typescript": "^2.0.0" + }, + "peerDependencies": { + "expo": "*", + "expo-font": "*", + "react": "*", + "react-native": "*" } }, - "node_modules/expo-splash-screen/node_modules/@expo/config-plugins": { - "version": "55.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.8.tgz", - "integrity": "sha512-8WfWTRntTCcowfOS+tHdB0z98gKetTwktg4G5TWkCkXVa8Jt1NUnvzaaU4UHk2vbR2U4N84RyZJFizSwfF6C9g==", + "node_modules/expo/node_modules/@expo/cli": { + "version": "55.0.30", "license": "MIT", "dependencies": { - "@expo/config-types": "^55.0.5", - "@expo/json-file": "~10.0.13", - "@expo/plist": "^0.5.2", - "@expo/sdk-runtime-versions": "^1.0.0", - "chalk": "^4.1.2", - "debug": "^4.3.5", + "@expo/code-signing-certificates": "^0.0.6", + "@expo/config": "~55.0.17", + "@expo/config-plugins": "~55.0.9", + "@expo/devcert": "^1.2.1", + "@expo/env": "~2.1.2", + "@expo/image-utils": "^0.8.14", + "@expo/json-file": "^10.0.14", + "@expo/log-box": "55.0.12", + "@expo/metro": "~55.1.1", + "@expo/metro-config": "~55.0.21", + "@expo/osascript": "^2.4.3", + "@expo/package-manager": "^1.10.5", + "@expo/plist": "^0.5.3", + "@expo/prebuild-config": "^55.0.18", + "@expo/require-utils": "^55.0.5", + "@expo/router-server": "^55.0.16", + "@expo/schema-utils": "^55.0.4", + "@expo/spawn-async": "^1.7.2", + "@expo/ws-tunnel": "^1.0.1", + "@expo/xcpretty": "^4.4.0", + "@react-native/dev-middleware": "0.83.6", + "accepts": "^1.3.8", + "arg": "^5.0.2", + "better-opn": "~3.0.2", + "bplist-creator": "0.1.0", + "bplist-parser": "^0.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.3.0", + "compression": "^1.7.4", + "connect": "^3.7.0", + "debug": "^4.3.4", + "dnssd-advertise": "^1.1.4", + "expo-server": "^55.0.9", + "fetch-nodeshim": "^0.4.10", "getenv": "^2.0.0", "glob": "^13.0.0", + "lan-network": "^0.2.1", + "multitars": "^1.0.0", + "node-forge": "^1.3.3", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "picomatch": "^4.0.3", + "pretty-format": "^29.7.0", + "progress": "^2.0.3", + "prompts": "^2.3.2", "resolve-from": "^5.0.0", - "semver": "^7.5.4", - "slugify": "^1.6.6", - "xcode": "^3.0.1", - "xml2js": "0.6.0" + "semver": "^7.6.0", + "send": "^0.19.0", + "slugify": "^1.3.4", + "source-map-support": "~0.5.21", + "stacktrace-parser": "^0.1.10", + "structured-headers": "^0.4.1", + "terminal-link": "^2.1.1", + "toqr": "^0.1.1", + "wrap-ansi": "^7.0.0", + "ws": "^8.12.1", + "zod": "^3.25.76" + }, + "bin": { + "expo-internal": "build/bin/cli" + }, + "peerDependencies": { + "expo": "*", + "expo-router": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo-router": { + "optional": true + }, + "react-native": { + "optional": true + } } }, - "node_modules/expo-splash-screen/node_modules/@expo/config-types": { - "version": "55.0.5", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-55.0.5.tgz", - "integrity": "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==", - "license": "MIT" - }, - "node_modules/expo-splash-screen/node_modules/@expo/plist": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.5.2.tgz", - "integrity": "sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g==", + "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/router-server": { + "version": "55.0.16", "license": "MIT", "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" + "debug": "^4.3.4" + }, + "peerDependencies": { + "@expo/metro-runtime": "^55.0.11", + "expo": "*", + "expo-constants": "^55.0.16", + "expo-font": "^55.0.7", + "expo-router": "*", + "expo-server": "^55.0.9", + "react": "*", + "react-dom": "*", + "react-server-dom-webpack": "~19.0.1 || ~19.1.2 || ~19.2.1" + }, + "peerDependenciesMeta": { + "@expo/metro-runtime": { + "optional": true + }, + "expo-router": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-server-dom-webpack": { + "optional": true + } } }, - "node_modules/expo-splash-screen/node_modules/@expo/prebuild-config": { - "version": "55.0.13", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-55.0.13.tgz", - "integrity": "sha512-3a0vS6dHhVEs8B9Sqz6OIdCZ52S7SWuvLxNTQ+LE66g8OJ5b8xW6kGSCK0Z2bWBFoYfAbZzitLaBi8oBKOVqkw==", + "node_modules/expo/node_modules/accepts": { + "version": "1.3.8", "license": "MIT", "dependencies": { - "@expo/config": "~55.0.13", - "@expo/config-plugins": "~55.0.8", - "@expo/config-types": "^55.0.5", - "@expo/image-utils": "^0.8.12", - "@expo/json-file": "^10.0.13", - "@react-native/normalize-colors": "0.83.4", - "debug": "^4.3.1", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "xml2js": "0.6.0" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, - "peerDependencies": { - "expo": "*" + "engines": { + "node": ">= 0.6" } }, - "node_modules/expo-splash-screen/node_modules/@react-native/normalize-colors": { - "version": "0.83.4", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.83.4.tgz", - "integrity": "sha512-9ezxaHjxqTkTOLg62SGg7YhFaE+fxa/jlrWP0nwf7eGFHlGOiTAaRR2KUfiN3K05e+EMbEhgcH/c7bgaXeGyJw==", - "license": "MIT" - }, - "node_modules/expo-splash-screen/node_modules/balanced-match": { + "node_modules/expo/node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": "18 || 20 || >=22" + } + }, + "node_modules/expo/node_modules/brace-expansion": { + "version": "5.0.6", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/expo/node_modules/ci-info": { + "version": "3.9.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/expo-splash-screen/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/expo/node_modules/expo-asset": { + "version": "55.0.17", "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "@expo/image-utils": "^0.8.14", + "expo-constants": "~55.0.16" }, - "engines": { - "node": "18 || 20 || >=22" + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" } }, - "node_modules/expo-splash-screen/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==", + "node_modules/expo/node_modules/expo-modules-core": { + "version": "55.0.25", "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-worklets": "^0.7.4 || ^0.8.0" + }, + "peerDependenciesMeta": { + "react-native-worklets": { + "optional": true + } } }, - "node_modules/expo-splash-screen/node_modules/glob": { + "node_modules/expo/node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", @@ -6550,10 +8555,8 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/expo-splash-screen/node_modules/minimatch": { + "node_modules/expo/node_modules/minimatch": { "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -6565,105 +8568,36 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/expo-status-bar": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-3.0.9.tgz", - "integrity": "sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw==", - "license": "MIT", - "dependencies": { - "react-native-is-edge-to-edge": "^1.2.1" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo/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==", + "node_modules/expo/node_modules/negotiator": { + "version": "0.6.3", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/expo/node_modules/expo-font": { - "version": "14.0.11", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", - "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", - "license": "MIT", - "dependencies": { - "fontfaceobserver": "^2.1.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" + "node": ">= 0.6" } }, - "node_modules/expo/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==", + "node_modules/expo/node_modules/react-refresh": { + "version": "0.14.2", "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": ">=0.10.0" } }, - "node_modules/expo/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/exponential-backoff": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", - "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", "license": "Apache-2.0" }, - "node_modules/fast-check": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.6.0.tgz", - "integrity": "sha512-h7H6Dm0Fy+H4ciQYFxFjXnXkzR2kr9Fb22c0UBpHnm59K2zpr2t13aPTHlltFiNT6zuxp6HMPAVVvgur4BLdpA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT", - "dependencies": { - "pure-rand": "^8.0.0" - }, - "engines": { - "node": ">=12.17.0" - } - }, "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==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "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", @@ -6678,9 +8612,6 @@ }, "node_modules/fast-glob/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==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -6691,8 +8622,6 @@ }, "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==", "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -6702,33 +8631,41 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, - "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" + "node_modules/fb-dotslash": { + "version": "0.5.8", + "license": "(MIT OR Apache-2.0)", + "bin": { + "dotslash": "bin/dotslash" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=20" } }, "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==", "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" @@ -6766,8 +8703,6 @@ }, "node_modules/fdir": { "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", "engines": { "node": ">=12.0.0" @@ -6781,13 +8716,45 @@ } } }, + "node_modules/fetch-nodeshim": { + "version": "0.4.10", + "license": "MIT" + }, + "node_modules/fetch-retry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-4.1.1.tgz", + "integrity": "sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA==", + "license": "MIT" + }, "node_modules/fflate": { "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "dev": true, "license": "MIT" }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -6801,10 +8768,29 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "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" @@ -6824,8 +8810,6 @@ }, "node_modules/finalhandler": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -6842,8 +8826,6 @@ }, "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" @@ -6851,14 +8833,10 @@ }, "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": { @@ -6889,28 +8867,49 @@ }, "node_modules/flatted": { "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, "node_modules/flow-enums-runtime": { "version": "0.0.6", - "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", - "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", "license": "MIT" }, "node_modules/fontfaceobserver": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", - "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", "license": "BSD-2-Clause" }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -6923,56 +8922,33 @@ "node": ">= 6" } }, - "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/freeport-async": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", - "integrity": "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==", + "node_modules/fresh": { + "version": "0.5.2", "license": "MIT", "engines": { - "node": ">=8" + "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==", + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=14.14" } }, "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==", "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" @@ -6980,8 +8956,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -6989,8 +8963,6 @@ }, "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==", "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -7000,7 +8972,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -7021,10 +8992,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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" + } + }, "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==", "license": "MIT", "engines": { "node": ">=8.0.0" @@ -7034,7 +9012,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -7044,10 +9021,23 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/getenv": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", - "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", "license": "MIT", "engines": { "node": ">=6" @@ -7055,9 +9045,6 @@ }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -7089,8 +9076,6 @@ }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -7099,8 +9084,6 @@ }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -7127,9 +9110,6 @@ }, "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", @@ -7146,11 +9126,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/golden-fleece": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/golden-fleece/-/golden-fleece-1.0.9.tgz", + "integrity": "sha512-YSwLaGMOgSBx9roJlNLL12c+FRiw7VECphinc6mGucphc/ZxTHgdEz6gmJqH6NOzYEd/yr64hwjom5pZ+tJVpg==" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7159,12 +9143,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "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/gradle-to-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gradle-to-js/-/gradle-to-js-2.0.1.tgz", + "integrity": "sha512-is3hDn9zb8XXnjbEeAEIqxTpLHUiGBqjegLmXPuyMBfKAggpadWFku4/AP8iYAGBX6qR9/5UIUIp47V0XI3aMw==", + "license": "Apache-2.0", + "dependencies": { + "lodash.merge": "^4.6.2" + }, + "bin": { + "gradle-to-js": "cli.js" + } + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -7172,10 +9191,32 @@ "dev": true, "license": "MIT" }, + "node_modules/graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -7185,7 +9226,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7198,7 +9238,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -7211,9 +9250,7 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7222,85 +9259,27 @@ "node": ">= 0.4" } }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } + "node_modules/hermes-compiler": { + "version": "0.14.1", + "license": "MIT" }, "node_modules/hermes-estree": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", - "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", + "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", "license": "MIT" }, "node_modules/hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", - "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", + "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", "license": "MIT", "dependencies": { - "hermes-estree": "0.29.1" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", - "license": "CC0-1.0" - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" + "hermes-estree": "0.32.0" } }, - "node_modules/hoist-non-react-statics/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/hosted-git-info": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" @@ -7311,34 +9290,38 @@ }, "node_modules/hosted-git-info/node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "node_modules/html-escaper": { + "version": "2.0.2", "dev": true, - "license": "MIT", + "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" + }, + "node_modules/http-call": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", + "integrity": "sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w==", + "license": "ISC", "dependencies": { - "whatwg-encoding": "^3.1.1" + "content-type": "^1.0.4", + "debug": "^4.1.1", + "is-retry-allowed": "^1.1.0", + "is-stream": "^2.0.0", + "parse-json": "^4.0.0", + "tunnel-agent": "^0.6.0" }, "engines": { - "node": ">=18" + "node": ">=8.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-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -7357,31 +9340,26 @@ }, "node_modules/http-errors/node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" }, "engines": { - "node": ">= 14" + "node": ">=10.19.0" } }, "node_modules/https-proxy-agent": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -7391,49 +9369,23 @@ "node": ">= 14" } }, - "node_modules/hyphenate-style-name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", - "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", - "license": "BSD-3-Clause" - }, - "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==", - "dev": true, + "node_modules/hyperlinker": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", + "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==", "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "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" - } - ], + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", "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==", "license": "MIT", "engines": { "node": ">= 4" @@ -7441,8 +9393,6 @@ }, "node_modules/image-size": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", "license": "MIT", "dependencies": { "queue": "6.0.2" @@ -7483,8 +9433,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "license": "MIT", "engines": { "node": ">=0.8.19" @@ -7494,7 +9442,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7502,9 +9449,6 @@ }, "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.", "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -7513,14 +9457,6 @@ }, "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/inline-style-prefixer": { @@ -7534,50 +9470,28 @@ }, "node_modules/invariant": { "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" } }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-arrayish": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", "license": "MIT" }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -7586,20 +9500,8 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-docker": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "license": "MIT", "bin": { "is-docker": "cli.js" @@ -7613,9 +9515,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7623,8 +9522,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -7632,9 +9529,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==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -7643,20 +9537,17 @@ "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=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" @@ -7664,34 +9555,47 @@ }, "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-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/is-wsl": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "license": "MIT", "dependencies": { "is-docker": "^2.0.0" @@ -7702,14 +9606,10 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "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==", "license": "BSD-3-Clause", "engines": { "node": ">=8" @@ -7717,8 +9617,6 @@ }, "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==", "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.12.3", @@ -7733,8 +9631,6 @@ }, "node_modules/istanbul-lib-instrument/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7742,8 +9638,6 @@ }, "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": { @@ -7757,8 +9651,6 @@ }, "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": { @@ -7769,26 +9661,40 @@ "node": ">=8" } }, - "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", - "dev": true, - "license": "MIT", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" } }, "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==", "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -7804,8 +9710,6 @@ }, "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==", "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7813,8 +9717,6 @@ }, "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==", "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -7836,26 +9738,8 @@ "fsevents": "^2.3.2" } }, - "node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", @@ -7872,42 +9756,8 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, "node_modules/jest-mock": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -7920,8 +9770,6 @@ }, "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==", "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7929,8 +9777,6 @@ }, "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==", "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -7944,10 +9790,21 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-util/node_modules/ci-info": { + "version": "3.9.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-util/node_modules/picomatch": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -7958,8 +9815,6 @@ }, "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==", "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -7969,46 +9824,12 @@ "leven": "^3.1.0", "pretty-format": "^29.7.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" + "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==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -8022,8 +9843,6 @@ }, "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==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -8037,21 +9856,45 @@ }, "node_modules/jimp-compact": { "version": "0.16.1", - "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", - "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", + "license": "MIT" + }, + "node_modules/jks-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jks-js/-/jks-js-1.1.0.tgz", + "integrity": "sha512-irWi8S2V029Vic63w0/TYa8NIZwXu9oeMtHQsX51JDIVBo0lrEaOoyM8ALEEh5PVKD6TrA26FixQK6TzT7dHqA==", + "license": "MIT", + "dependencies": { + "node-forge": "^1.3.1", + "node-int64": "^0.4.0", + "node-rsa": "^1.1.1" + } + }, + "node_modules/joi": { + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/join-component": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", + "integrity": "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==", "license": "MIT" }, "node_modules/js-tokens": { "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -8062,55 +9905,19 @@ }, "node_modules/jsc-safe-url": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", - "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", "license": "0BSD" }, - "node_modules/jsdom": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", - "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", - "dev": true, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "license": "MIT", - "dependencies": { - "cssstyle": "^4.1.0", - "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^2.11.2" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "node": ">= 10.16.0" } }, "node_modules/jsesc": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -8123,7 +9930,12 @@ "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-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -8142,8 +9954,6 @@ }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -8152,11 +9962,28 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keychain": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/keychain/-/keychain-1.5.0.tgz", + "integrity": "sha512-liyp4r+93RI7EB2jhwaRd4MWfdgHH6shuldkaPMkELCJjMFvOOVXuTvw1pGqFfhsrgA6OqfykWWPQgBjQakVag==", + "license": "MIT" + }, "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" @@ -8164,17 +9991,13 @@ }, "node_modules/kleur": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/lan-network": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz", - "integrity": "sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ==", + "version": "0.2.1", "license": "MIT", "bin": { "lan-network": "dist/lan-network-cli.js" @@ -8182,8 +10005,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==", "license": "MIT", "engines": { "node": ">=6" @@ -8205,8 +10026,6 @@ }, "node_modules/lighthouse-logger": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", - "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", "license": "Apache-2.0", "dependencies": { "debug": "^2.6.9", @@ -8215,8 +10034,6 @@ }, "node_modules/lighthouse-logger/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" @@ -8224,14 +10041,10 @@ }, "node_modules/lighthouse-logger/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/lightningcss": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -8257,150 +10070,8 @@ "lightningcss-win32-x64-msvc": "1.32.0" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "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.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], @@ -8419,8 +10090,6 @@ }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], @@ -8437,44 +10106,16 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", "engines": { - "node": ">= 12.0.0" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -8485,8 +10126,6 @@ }, "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": { @@ -8499,29 +10138,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", "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/lodash.throttle": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", "license": "MIT" }, "node_modules/log-symbols": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "license": "MIT", "dependencies": { "chalk": "^2.0.1" @@ -8532,8 +10177,6 @@ }, "node_modules/log-symbols/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -8544,8 +10187,6 @@ }, "node_modules/log-symbols/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", @@ -8558,8 +10199,6 @@ }, "node_modules/log-symbols/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "license": "MIT", "engines": { "node": ">=0.8.0" @@ -8567,8 +10206,6 @@ }, "node_modules/log-symbols/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "license": "MIT", "engines": { "node": ">=4" @@ -8576,8 +10213,6 @@ }, "node_modules/log-symbols/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -8588,8 +10223,6 @@ }, "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" @@ -8600,28 +10233,19 @@ }, "node_modules/loose-envify/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/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==", + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "license": "MIT", - "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=8" } }, "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==", "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -8629,8 +10253,6 @@ }, "node_modules/magic-string": { "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8639,8 +10261,6 @@ }, "node_modules/magicast": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", - "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8651,8 +10271,6 @@ }, "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": { @@ -8665,10 +10283,14 @@ "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==", + "license": "ISC" + }, "node_modules/makeerror": { "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" @@ -8676,94 +10298,80 @@ }, "node_modules/marky": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", - "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", "license": "Apache-2.0" }, "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==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/memoize-one": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", "license": "MIT" }, - "node_modules/merge-options": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", - "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", - "license": "MIT", - "dependencies": { - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, "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==", "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/metro": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", - "integrity": "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==", + "version": "0.83.7", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", + "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "@babel/types": "^7.25.2", - "accepts": "^1.3.7", - "chalk": "^4.0.0", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", - "hermes-parser": "0.32.0", + "hermes-parser": "0.35.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3", - "mime-types": "^2.1.27", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-config": "0.83.7", + "metro-core": "0.83.7", + "metro-file-map": "0.83.7", + "metro-resolver": "0.83.7", + "metro-runtime": "0.83.7", + "metro-source-map": "0.83.7", + "metro-symbolicate": "0.83.7", + "metro-transform-plugins": "0.83.7", + "metro-transform-worker": "0.83.7", + "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", @@ -8779,14 +10387,13 @@ } }, "node_modules/metro-babel-transformer": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz", - "integrity": "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==", + "version": "0.83.7", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.32.0", + "hermes-parser": "0.35.0", + "metro-cache-key": "0.83.7", "nullthrows": "^1.1.1" }, "engines": { @@ -8794,39 +10401,31 @@ } }, "node_modules/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "version": "0.35.0", "license": "MIT" - }, - "node_modules/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + }, + "node_modules/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.35.0", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.0" + "hermes-estree": "0.35.0" } }, "node_modules/metro-cache": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.3.tgz", - "integrity": "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==", + "version": "0.83.7", "license": "MIT", "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.3" + "metro-core": "0.83.7" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-cache-key": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.3.tgz", - "integrity": "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==", + "version": "0.83.7", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -8836,18 +10435,16 @@ } }, "node_modules/metro-config": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.3.tgz", - "integrity": "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==", + "version": "0.83.7", "license": "MIT", "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", - "metro": "0.83.3", - "metro-cache": "0.83.3", - "metro-core": "0.83.3", - "metro-runtime": "0.83.3", + "metro": "0.83.7", + "metro-cache": "0.83.7", + "metro-core": "0.83.7", + "metro-runtime": "0.83.7", "yaml": "^2.6.1" }, "engines": { @@ -8855,23 +10452,19 @@ } }, "node_modules/metro-core": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.3.tgz", - "integrity": "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==", + "version": "0.83.7", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.3" + "metro-resolver": "0.83.7" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-file-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.3.tgz", - "integrity": "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==", + "version": "0.83.7", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -8889,9 +10482,7 @@ } }, "node_modules/metro-minify-terser": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz", - "integrity": "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==", + "version": "0.83.7", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", @@ -8902,9 +10493,7 @@ } }, "node_modules/metro-resolver": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.3.tgz", - "integrity": "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==", + "version": "0.83.7", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -8914,9 +10503,7 @@ } }, "node_modules/metro-runtime": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.3.tgz", - "integrity": "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==", + "version": "0.83.7", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", @@ -8927,19 +10514,16 @@ } }, "node_modules/metro-source-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.3.tgz", - "integrity": "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==", + "version": "0.83.7", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.3", - "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", - "@babel/types": "^7.25.2", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-symbolicate": "0.83.3", + "metro-symbolicate": "0.83.7", "nullthrows": "^1.1.1", - "ob1": "0.83.3", + "ob1": "0.83.7", "source-map": "^0.5.6", "vlq": "^1.0.0" }, @@ -8948,14 +10532,12 @@ } }, "node_modules/metro-symbolicate": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz", - "integrity": "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==", + "version": "0.83.7", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-source-map": "0.83.3", + "metro-source-map": "0.83.7", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" @@ -8968,15 +10550,13 @@ } }, "node_modules/metro-transform-plugins": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz", - "integrity": "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==", + "version": "0.83.7", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" }, @@ -8985,54 +10565,61 @@ } }, "node_modules/metro-transform-worker": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz", - "integrity": "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==", + "version": "0.83.7", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/types": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-source-map": "0.83.3", - "metro-transform-plugins": "0.83.3", + "metro": "0.83.7", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-minify-terser": "0.83.7", + "metro-source-map": "0.83.7", + "metro-transform-plugins": "0.83.7", "nullthrows": "^1.1.1" }, "engines": { "node": ">=20.19.4" } }, - "node_modules/metro/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT" - }, "node_modules/metro/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "version": "0.35.0", "license": "MIT" }, "node_modules/metro/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "version": "0.35.0", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.0" + "hermes-estree": "0.35.0" + } + }, + "node_modules/metro/node_modules/mime-db": { + "version": "1.54.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro/node_modules/mime-types": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/metro/node_modules/ws": { "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "license": "MIT", "engines": { "node": ">=8.3.0" @@ -9052,8 +10639,6 @@ }, "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", @@ -9065,8 +10650,6 @@ }, "node_modules/micromatch/node_modules/picomatch": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -9077,8 +10660,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" @@ -9088,9 +10669,7 @@ } }, "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==", + "version": "1.52.0", "license": "MIT", "engines": { "node": ">= 0.6" @@ -9098,8 +10677,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" @@ -9108,29 +10685,17 @@ "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": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/min-indent": { + "node_modules/mimic-response": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "license": "MIT", "engines": { "node": ">=4" @@ -9140,6 +10705,7 @@ "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" @@ -9156,35 +10722,106 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", + "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", "license": "MIT", "dependencies": { - "minipass": "^7.1.2" + "minipass": "^7.0.4", + "rimraf": "^5.0.5" }, "engines": { "node": ">= 18" } }, + "node_modules/minizlib/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minizlib/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/minizlib/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minizlib/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minizlib/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "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" @@ -9193,10 +10830,22 @@ "node": ">=10" } }, + "node_modules/mobile": { + "resolved": "packages/mobile", + "link": true + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, "node_modules/mrmime": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", "dev": true, "license": "MIT", "engines": { @@ -9205,10 +10854,108 @@ }, "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/multipasta": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.7.tgz", + "integrity": "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==", + "license": "MIT" + }, + "node_modules/multitars": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mv/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -9220,10 +10967,15 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nan": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", + "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==", + "license": "MIT", + "optional": true + }, "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", @@ -9245,21 +10997,32 @@ "dev": true, "license": "MIT" }, + "node_modules/natural-orderby": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-2.0.3.tgz", + "integrity": "sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "license": "MIT", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, "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==", + "version": "1.0.0", "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/nested-error-stacks": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", - "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", - "license": "MIT" - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -9280,32 +11043,8 @@ } } }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-forge": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -9313,29 +11052,55 @@ }, "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==", "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.44", "license": "MIT" }, + "node_modules/node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", + "license": "MIT", + "dependencies": { + "asn1": "^0.2.4" + } + }, + "node_modules/node-stream-zip": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/antelle" + } + }, "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/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-package-arg": { "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", - "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", "license": "ISC", "dependencies": { "hosted-git-info": "^7.0.0", @@ -9349,21 +11114,10 @@ }, "node_modules/nullthrows": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "license": "MIT" - }, - "node_modules/nwsapi": { - "version": "2.2.23", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", - "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", - "dev": true, "license": "MIT" }, "node_modules/ob1": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.3.tgz", - "integrity": "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==", + "version": "0.83.7", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -9381,10 +11135,17 @@ "node": ">=0.10.0" } }, + "node_modules/object-treeify": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", + "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/obug": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", @@ -9394,8 +11155,6 @@ }, "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" @@ -9406,8 +11165,6 @@ }, "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" @@ -9415,8 +11172,6 @@ }, "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" @@ -9424,8 +11179,6 @@ }, "node_modules/onetime": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "license": "MIT", "dependencies": { "mimic-fn": "^1.0.0" @@ -9436,8 +11189,6 @@ }, "node_modules/open": { "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "license": "MIT", "dependencies": { "is-docker": "^2.0.0", @@ -9470,8 +11221,6 @@ }, "node_modules/ora": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", - "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", "license": "MIT", "dependencies": { "chalk": "^2.4.2", @@ -9487,8 +11236,6 @@ }, "node_modules/ora/node_modules/ansi-regex": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "license": "MIT", "engines": { "node": ">=6" @@ -9496,8 +11243,6 @@ }, "node_modules/ora/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -9508,8 +11253,6 @@ }, "node_modules/ora/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", @@ -9522,17 +11265,13 @@ }, "node_modules/ora/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/ora/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "3.0.0", "license": "MIT", "engines": { "node": ">=4" @@ -9540,8 +11279,6 @@ }, "node_modules/ora/node_modules/strip-ansi": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" @@ -9552,8 +11289,6 @@ }, "node_modules/ora/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -9562,10 +11297,18 @@ "node": ">=4" } }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "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" @@ -9579,8 +11322,6 @@ }, "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": { @@ -9595,13 +11336,17 @@ }, "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==", "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9615,35 +11360,21 @@ "node": ">=6" } }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "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" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=4" } }, - "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-png": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", - "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", "license": "MIT", "dependencies": { "pngjs": "^3.3.0" @@ -9652,32 +11383,25 @@ "node": ">=10" } }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, "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/password-prompt": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.3.tgz", + "integrity": "sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==", + "license": "0BSD", + "dependencies": { + "ansi-escapes": "^4.3.2", + "cross-spawn": "^7.0.3" + } + }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -9685,8 +11409,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9694,8 +11416,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -9703,14 +11423,10 @@ }, "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==", "license": "MIT" }, "node_modules/path-scurry": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", @@ -9724,9 +11440,7 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.3.tgz", - "integrity": "sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==", + "version": "11.4.0", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -9734,9 +11448,6 @@ }, "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" @@ -9744,24 +11455,18 @@ }, "node_modules/pathe": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "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": "3.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz", - "integrity": "sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==", + "version": "4.0.4", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -9769,20 +11474,80 @@ }, "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==", "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==", + "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==", + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "version": "3.1.1", "license": "MIT", "dependencies": { - "@xmldom/xmldom": "^0.8.8", + "@xmldom/xmldom": "^0.9.10", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" }, @@ -9790,19 +11555,23 @@ "node": ">=10.4.0" } }, + "node_modules/plist/node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, "node_modules/pngjs": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.14", + "dev": true, "funding": [ { "type": "opencollective", @@ -9819,7 +11588,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -9844,9 +11613,9 @@ } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { @@ -9859,58 +11628,24 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/pretty-format/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/pretty-format/node_modules/@sinclair/typebox": { - "version": "0.34.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", - "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", - "dev": true, - "license": "MIT" - }, "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" @@ -9923,22 +11658,10 @@ "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/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/proc-log": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -9946,8 +11669,6 @@ }, "node_modules/progress": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "license": "MIT", "engines": { "node": ">=0.4.0" @@ -9955,17 +11676,32 @@ }, "node_modules/promise": { "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", "license": "MIT", "dependencies": { "asap": "~2.0.6" } }, + "node_modules/promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==", + "license": "ISC" + }, + "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", + "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==", "license": "MIT", "dependencies": { "kleur": "^3.0.3", @@ -9975,14 +11711,14 @@ "node": ">= 6" } }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "node_modules/punycode": { @@ -9994,31 +11730,143 @@ "node": ">=6" } }, - "node_modules/pure-rand": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.0.tgz", - "integrity": "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qrcode-terminal": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", - "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, + "node_modules/qrcode": { + "version": "1.5.4", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode-terminal": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/qrcode/node_modules/camelcase": { + "version": "5.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/pngjs": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/query-string": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", @@ -10039,8 +11887,6 @@ }, "node_modules/queue": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", "license": "MIT", "dependencies": { "inherits": "~2.0.3" @@ -10048,9 +11894,6 @@ }, "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", @@ -10067,43 +11910,27 @@ ], "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==", + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "license": "MIT", "engines": { - "node": ">= 0.6" - } - }, - "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" + "node": ">=10" }, - "bin": { - "rc": "cli.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/range-parser": { + "version": "1.2.1", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.2.0", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10111,8 +11938,6 @@ }, "node_modules/react-devtools-core": { "version": "6.1.5", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", - "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", "license": "MIT", "dependencies": { "shell-quote": "^1.6.1", @@ -10121,8 +11946,6 @@ }, "node_modules/react-devtools-core/node_modules/ws": { "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "license": "MIT", "engines": { "node": ">=8.3.0" @@ -10141,74 +11964,67 @@ } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.2.0" } }, - "node_modules/react-freeze": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", - "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=17.0.0" - } + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" }, "node_modules/react-is": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz", - "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==", + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", + "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", "license": "MIT" }, "node_modules/react-native": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", - "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", + "version": "0.83.6", "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.7.0", - "@react-native/assets-registry": "0.81.5", - "@react-native/codegen": "0.81.5", - "@react-native/community-cli-plugin": "0.81.5", - "@react-native/gradle-plugin": "0.81.5", - "@react-native/js-polyfills": "0.81.5", - "@react-native/normalize-colors": "0.81.5", - "@react-native/virtualized-lists": "0.81.5", + "@react-native/assets-registry": "0.83.6", + "@react-native/codegen": "0.83.6", + "@react-native/community-cli-plugin": "0.83.6", + "@react-native/gradle-plugin": "0.83.6", + "@react-native/js-polyfills": "0.83.6", + "@react-native/normalize-colors": "0.83.6", + "@react-native/virtualized-lists": "0.83.6", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", - "babel-plugin-syntax-hermes-parser": "0.29.1", + "babel-plugin-syntax-hermes-parser": "0.32.0", "base64-js": "^1.5.1", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", + "hermes-compiler": "0.14.1", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", - "metro-runtime": "^0.83.1", - "metro-source-map": "^0.83.1", + "metro-runtime": "^0.83.6", + "metro-source-map": "^0.83.6", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.1.5", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", - "scheduler": "0.26.0", + "scheduler": "0.27.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", - "ws": "^6.2.3", + "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { @@ -10218,8 +12034,8 @@ "node": ">= 20.19.4" }, "peerDependencies": { - "@types/react": "^19.1.0", - "react": "^19.1.0" + "@types/react": "^19.1.1", + "react": "^19.2.0" }, "peerDependenciesMeta": { "@types/react": { @@ -10229,53 +12045,7 @@ }, "node_modules/react-native-is-edge-to-edge": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz", - "integrity": "sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-paper": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.15.0.tgz", - "integrity": "sha512-I/1CQLfW9VM0Oo5I5dQI/hjgf1I6q2S1wwgzAdsv6whAQ3zO97GWHwtgNh9se9j8zBOJ86afPTQKxxUL0IJd9A==", - "license": "MIT", - "workspaces": [ - "example", - "docs" - ], - "dependencies": { - "@callstack/react-theme-provider": "^3.0.9", - "color": "^3.1.2", - "use-latest-callback": "^0.2.3" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-safe-area-context": "*" - } - }, - "node_modules/react-native-safe-area-context": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.7.0.tgz", - "integrity": "sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-screens": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.24.0.tgz", - "integrity": "sha512-SyoiGaDofiyGPFrUkn1oGsAzkRuX1JUvTD9YQQK3G1JGQ5VWkvHgYSsc1K9OrLsDQxN7NmV71O0sHCAh8cBetA==", "license": "MIT", - "dependencies": { - "react-freeze": "^1.0.0", - "warn-once": "^0.1.0" - }, "peerDependencies": { "react": "*", "react-native": "*" @@ -10313,127 +12083,122 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, - "node_modules/react-native/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==", + "node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "dependencies": { + "hermes-parser": "0.32.0" } }, "node_modules/react-native/node_modules/commander": { "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", "engines": { "node": ">=18" } }, - "node_modules/react-native/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==", + "node_modules/react-native/node_modules/react-refresh": { + "version": "0.14.2", "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": ">=0.10.0" } }, - "node_modules/react-native/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-native/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "7.5.10", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/react-syntax-highlighter": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz", - "integrity": "sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==", + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "highlight.js": "^10.4.1", - "highlightjs-vue": "^1.0.0", - "lowlight": "^1.17.0", - "prismjs": "^1.30.0", - "refractor": "^5.0.0" + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" }, "engines": { - "node": ">= 16.20.2" + "node": ">=10" }, "peerDependencies": { - "react": ">= 0.14.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/react-test-renderer": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.1.0.tgz", - "integrity": "sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==", - "dev": true, + "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-is": "^19.1.0", - "scheduler": "^0.26.0" + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" }, "peerDependencies": { - "react": "^19.1.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, + "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": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/refractor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", - "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", "license": "MIT", "dependencies": { - "@types/hast": "^3.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^9.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "esprima": "~4.0.0" } }, "node_modules/regenerate": { @@ -10456,8 +12221,6 @@ }, "node_modules/regenerator-runtime": { "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "license": "MIT" }, "node_modules/regexpu-core": { @@ -10495,10 +12258,14 @@ "regjsparser": "bin/parser" } }, + "node_modules/remove-trailing-slash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", + "integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==", + "license": "MIT" + }, "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==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10513,34 +12280,15 @@ "node": ">=0.10.0" } }, - "node_modules/requireg": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", - "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", - "dependencies": { - "nested-error-stacks": "~2.0.1", - "rc": "~1.2.7", - "resolve": "~1.7.1" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/requireg/node_modules/resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", - "license": "MIT", - "dependencies": { - "path-parse": "^1.0.5" - } + "node_modules/require-main-filename": { + "version": "2.0.0", + "license": "ISC" }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -10555,10 +12303,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -10566,23 +12318,22 @@ }, "node_modules/resolve-workspace-root": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.1.tgz", - "integrity": "sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==", "license": "MIT" }, - "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==", + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/restore-cursor": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "license": "MIT", "dependencies": { "onetime": "^2.0.0", @@ -10592,11 +12343,17 @@ "node": ">=4" } }, + "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", + "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", @@ -10605,9 +12362,6 @@ }, "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", "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -10621,8 +12375,6 @@ }, "node_modules/rolldown": { "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", - "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", "dev": true, "license": "MIT", "dependencies": { @@ -10653,18 +12405,8 @@ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" } }, - "node_modules/rrweb-cssom": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", - "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", - "dev": true, - "license": "MIT" - }, "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", @@ -10686,8 +12428,6 @@ }, "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", @@ -10704,45 +12444,36 @@ ], "license": "MIT" }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "license": "MIT", + "optional": true + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, "license": "MIT" }, "node_modules/sax": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -10753,8 +12484,6 @@ }, "node_modules/send": { "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -10777,8 +12506,6 @@ }, "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" @@ -10786,14 +12513,10 @@ }, "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": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -10801,8 +12524,6 @@ }, "node_modules/send/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" @@ -10813,8 +12534,6 @@ }, "node_modules/send/node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -10822,8 +12541,6 @@ }, "node_modules/serialize-error": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", - "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10831,8 +12548,6 @@ }, "node_modules/serve-static": { "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", @@ -10846,13 +12561,30 @@ }, "node_modules/serve-static/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/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", + "license": "MIT" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/set-interval-async": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/set-interval-async/-/set-interval-async-3.0.3.tgz", + "integrity": "sha512-o4DyBv6mko+A9cH3QKek4SAAT5UyJRkfdTi6JHii6ZCKUYFun8SwgBmQrOXd158JOwBQzA+BnO8BvT64xuCaSw==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -10861,8 +12593,6 @@ }, "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/sf-symbols-typescript": { @@ -10874,10 +12604,14 @@ "node": ">=10" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "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==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -10888,8 +12622,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -10897,8 +12629,6 @@ }, "node_modules/shell-quote": { "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -10909,27 +12639,21 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, "node_modules/simple-git": { - "version": "3.35.2", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.35.2.tgz", - "integrity": "sha512-ZMjl06lzTm1EScxEGuM6+mEX+NQd14h/B3x0vWU+YOXAMF8sicyi1K4cjTfj5is+35ChJEHDl1EjypzYFWH2FA==", + "version": "3.36.0", "license": "MIT", "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", - "@simple-git/args-pathspec": "^1.0.2", - "@simple-git/argv-parser": "^1.0.3", + "@simple-git/args-pathspec": "^1.0.3", + "@simple-git/argv-parser": "^1.1.0", "debug": "^4.4.0" }, "funding": { @@ -10939,8 +12663,6 @@ }, "node_modules/simple-plist": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", - "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", "license": "MIT", "dependencies": { "bplist-creator": "0.1.0", @@ -10948,18 +12670,6 @@ "plist": "^3.0.5" } }, - "node_modules/simple-plist/node_modules/bplist-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", - "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", - "license": "MIT", - "dependencies": { - "big-integer": "1.6.x" - }, - "engines": { - "node": ">= 5.10.0" - } - }, "node_modules/simple-swizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", @@ -10971,124 +12681,54 @@ }, "node_modules/sirv": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", - "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", "dev": true, "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slugify": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.9.tgz", - "integrity": "sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/socket.io": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", - "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" + "mrmime": "^2.0.0", + "totalist": "^3.0.0" }, "engines": { - "node": ">=10.2.0" + "node": ">=18" } }, - "node_modules/socket.io-adapter": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", - "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", - "license": "MIT", - "dependencies": { - "debug": "~4.4.1", - "ws": "~8.18.3" - } + "node_modules/sisteransi": { + "version": "1.0.5", + "license": "MIT" }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "node_modules/slash": { + "version": "3.0.0", "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": ">=8" } }, - "node_modules/socket.io-client": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", - "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "license": "MIT", "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/socket.io-parser": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", - "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", + "node_modules/slugify": { + "version": "1.6.9", "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1" - }, "engines": { - "node": ">=10.0.0" + "node": ">=8.0.0" } }, "node_modules/source-map": { "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -11096,8 +12736,6 @@ }, "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" @@ -11105,8 +12743,6 @@ }, "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==", "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -11115,23 +12751,11 @@ }, "node_modules/source-map-support/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==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -11143,14 +12767,10 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "license": "BSD-3-Clause" }, "node_modules/stack-utils": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" @@ -11161,8 +12781,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -11170,21 +12788,15 @@ }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/stackframe": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "license": "MIT" }, "node_modules/stacktrace-parser": { "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", "license": "MIT", "dependencies": { "type-fest": "^0.7.1" @@ -11195,8 +12807,6 @@ }, "node_modules/stacktrace-parser/node_modules/type-fest": { "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" @@ -11204,8 +12814,6 @@ }, "node_modules/statuses": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -11213,20 +12821,27 @@ }, "node_modules/std-env": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", - "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, "license": "MIT" }, "node_modules/stream-buffers": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", - "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", "license": "Unlicense", "engines": { "node": ">= 0.10.0" } }, + "node_modules/streamx": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", + "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", @@ -11237,6 +12852,19 @@ } }, "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", @@ -11252,8 +12880,6 @@ }, "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==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11262,14 +12888,14 @@ "node": ">=8" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { - "min-indent": "^1.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -11290,8 +12916,6 @@ }, "node_modules/structured-headers": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", - "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", "license": "MIT" }, "node_modules/styleq": { @@ -11301,17 +12925,17 @@ "license": "MIT" }, "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", + "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { @@ -11331,10 +12955,66 @@ "node": ">= 6" } }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/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==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -11345,8 +13025,6 @@ }, "node_modules/supports-hyperlinks": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0", @@ -11358,8 +13036,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -11368,17 +13044,23 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/tar": { - "version": "7.5.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", - "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -11391,6 +13073,29 @@ "node": ">=18" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/tar/node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -11400,10 +13105,17 @@ "node": ">=18" } }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/terminal-link": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", @@ -11417,9 +13129,7 @@ } }, "node_modules/terser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", - "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "version": "5.47.1", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -11436,14 +13146,10 @@ }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, "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==", "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", @@ -11455,9 +13161,7 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -11466,8 +13170,6 @@ }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -11476,10 +13178,17 @@ "node": "*" } }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "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" }, @@ -11506,21 +13215,15 @@ }, "node_modules/throat": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "dev": true, "license": "MIT", "engines": { @@ -11529,8 +13232,6 @@ }, "node_modules/tinyglobby": { "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -11543,58 +13244,20 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tinyrainbow": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "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==", "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" @@ -11605,48 +13268,28 @@ }, "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/toqr": { + "version": "0.1.1", + "license": "MIT" + }, "node_modules/totalist": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" }, "node_modules/ts-api-utils": { "version": "1.4.3", @@ -11661,19 +13304,105 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-deepmerge": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-6.2.0.tgz", + "integrity": "sha512-2qxI/FZVDPbzh63GwWIZYE7daWKtwXZYuyc8YNq0iTmMUwn4mL0jRLsp6hfFlgbdRSR4x2ppe+E86FnvEpN7Nw==", + "license": "ISC", + "engines": { + "node": ">=14.13.1" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "license": "Apache-2.0" }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "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==", - "dev": true, - "license": "0BSD", - "optional": true + "license": "0BSD" + }, + "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/turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "license": "MIT", + "dependencies": { + "domino": "^2.1.6" + } }, "node_modules/type-check": { "version": "0.4.0", @@ -11690,8 +13419,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=4" @@ -11712,9 +13439,7 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -11750,19 +13475,8 @@ "node": "*" } }, - "node_modules/undici": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", - "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, "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==", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -11805,19 +13519,45 @@ "node": ">=4" } }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "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/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -11848,12 +13588,32 @@ "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/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" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-latest-callback": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", @@ -11863,6 +13623,28 @@ "react": ">=16.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", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -11874,8 +13656,6 @@ }, "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" @@ -11883,17 +13663,19 @@ }, "node_modules/uuid": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, "node_modules/validate-npm-package-name": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -11901,17 +13683,26 @@ }, "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/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/vite": { "version": "8.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", - "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "dev": true, "license": "MIT", "dependencies": { @@ -11986,52 +13777,8 @@ } } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vite/node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/vitest": { "version": "4.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz", - "integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==", "dev": true, "license": "MIT", "dependencies": { @@ -12118,42 +13865,12 @@ } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vlq": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", "license": "MIT" }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/walker": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" @@ -12167,94 +13884,37 @@ }, "node_modules/wcwidth": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "license": "MIT", "dependencies": { "defaults": "^1.0.3" } }, "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" }, "node_modules/whatwg-fetch": { "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "license": "MIT" }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url-without-unicode": { - "version": "8.0.0-3", - "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", - "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { - "buffer": "^5.4.3", - "punycode": "^2.1.1", - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=10" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "node_modules/whatwg-url-without-unicode/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" - } + "node_modules/whatwg-url-minimum": { + "version": "0.1.2", + "license": "MIT" }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -12266,10 +13926,12 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "license": "ISC" + }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { @@ -12283,6 +13945,18 @@ "node": ">=8" } }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wonka": { "version": "6.3.6", "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.6.tgz", @@ -12299,7 +13973,29 @@ "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==", + "license": "MIT" + }, "node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", @@ -12318,14 +14014,10 @@ }, "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==", "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", @@ -12337,8 +14029,6 @@ }, "node_modules/ws": { "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -12358,8 +14048,6 @@ }, "node_modules/xcode": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", - "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", "license": "Apache-2.0", "dependencies": { "simple-plist": "^1.1.0", @@ -12369,20 +14057,8 @@ "node": ">=10.0.0" } }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, "node_modules/xml2js": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", - "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", "license": "MIT", "dependencies": { "sax": ">=0.6.0", @@ -12394,8 +14070,6 @@ }, "node_modules/xml2js/node_modules/xmlbuilder": { "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "license": "MIT", "engines": { "node": ">=4.0" @@ -12403,32 +14077,13 @@ }, "node_modules/xmlbuilder": { "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "license": "MIT", "engines": { "node": ">=8.0" } }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", "engines": { "node": ">=10" @@ -12436,14 +14091,10 @@ }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.9.0", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -12457,8 +14108,6 @@ }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -12475,17 +14124,23 @@ }, "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==", "license": "ISC", "engines": { "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "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" @@ -12494,136 +14149,120 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/mobile-client": { - "name": "@codelink/mobile-client", - "version": "0.1.0", - "dependencies": { - "@callstack/react-theme-provider": "^3.0.9", - "@codelink/protocol": "*", - "@expo-google-fonts/inter": "^0.2.3", - "@expo-google-fonts/manrope": "^0.2.3", - "@expo-google-fonts/space-grotesk": "^0.2.3", - "@expo/vector-icons": "^15.0.3", - "@react-native-async-storage/async-storage": "^2.1.0", - "@react-navigation/bottom-tabs": "^7.15.9", - "@react-navigation/native": "^7.2.2", - "@types/react-syntax-highlighter": "^15.5.13", - "color": "^3.2.1", - "expo": "~54.0.33", - "expo-asset": "^12.0.12", - "expo-blur": "~15.0.8", - "expo-constants": "^18.0.13", - "expo-font": "~12.0.10", - "expo-haptics": "~15.0.8", - "expo-linear-gradient": "~13.0.2", - "expo-modules-core": "^3.0.29", - "expo-splash-screen": "^55.0.14-canary-20260328-2049187", - "expo-status-bar": "~3.0.9", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-native": "0.81.5", - "react-native-paper": "^5.14.5", - "react-native-safe-area-context": "^5.6.2", - "react-native-screens": "^4.24.0", - "react-native-web": "^0.21.0", - "react-syntax-highlighter": "^16.1.1", - "socket.io-client": "^4.8.1" + "node_modules/zod": { + "version": "3.25.76", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.13.tgz", + "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" }, - "devDependencies": { - "@testing-library/react-native": "^13.3.3", - "@types/react": "~19.0.12", - "babel-plugin-react-native-web": "^0.19.12", - "babel-preset-expo": "^54.0.10", - "fast-check": "^3.15.0", - "jsdom": "^25.0.1", - "react-test-renderer": "19.1.0", - "typescript": "~5.9.2", - "vitest": "^4.1.0" + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } } }, - "packages/mobile-client/node_modules/expo-font": { - "version": "12.0.10", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.10.tgz", - "integrity": "sha512-Q1i2NuYri3jy32zdnBaHHCya1wH1yMAsI+3CCmj9zlQzlhsS9Bdwcj2W3c5eU5FvH2hsNQy4O+O1NnM6o/pDaQ==", + "node_modules/zxing-wasm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/zxing-wasm/-/zxing-wasm-3.0.3.tgz", + "integrity": "sha512-DdOn/G5F+qvZELWeO5ZFFwcN611TfMybxPV0LUUoutUmiH2t47MZSB7gLV9O9YLhvudBdnzQNAoFOu4Xz8eOrQ==", "license": "MIT", "dependencies": { - "fontfaceobserver": "^2.1.0" + "@types/emscripten": "^1.41.5", + "type-fest": "^5.6.0" }, "peerDependencies": { - "expo": "*" + "@types/emscripten": ">=1.39.6" } }, - "packages/mobile-client/node_modules/fast-check": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", - "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT", + "node_modules/zxing-wasm/node_modules/type-fest": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.6.0.tgz", + "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==", + "license": "(MIT OR CC0-1.0)", "dependencies": { - "pure-rand": "^6.1.0" + "tagged-tag": "^1.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/mobile-client/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" + "packages/mobile": { + "version": "1.0.0", + "dependencies": { + "@codelink/protocol": "^2.0.0", + "@expo/metro-runtime": "~55.0.11", + "@expo/ngrok": "^4.1.3", + "babel-preset-expo": "^55.0.21", + "diff-match-patch": "^1.0.5", + "eas-cli": "^18.13.1", + "expo": "~55.0.24", + "expo-camera": "~55.0.18", + "expo-router": "~55.0.14", + "expo-secure-store": "~55.0.14", + "expo-status-bar": "~55.0.6", + "react": "19.2.0", + "react-dom": "19.2.0", + "react-native": "0.83.6", + "react-native-web": "^0.21.0", + "zustand": "^5.0.13" + }, + "devDependencies": { + "@types/diff-match-patch": "^1.0.36", + "@types/react": "~19.2.2", + "typescript": "~5.9.2" + } }, "packages/protocol": { "name": "@codelink/protocol", - "version": "0.1.0", + "version": "2.0.0", "devDependencies": { "typescript": "^5.0.0", - "vitest": "^4.1.0" - } - }, - "packages/relay-server": { - "name": "@codelink/relay-server", - "version": "0.1.0", - "dependencies": { - "@codelink/protocol": "*", - "socket.io": "^4.0.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.0.0" + "vitest": "^4.0.0" } }, "packages/vscode-extension": { "name": "codelink-extension", - "version": "0.1.0", + "version": "2.0.0", "dependencies": { - "@codelink/protocol": "*", + "@codelink/protocol": "^2.0.0", + "diff-match-patch": "^1.0.5", + "qrcode": "^1.5.4", "simple-git": "^3.25.0", - "socket.io-client": "^4.8.3" + "ws": "^8.18.0" }, "devDependencies": { - "@types/node": "^25.3.0", + "@types/diff-match-patch": "^1.0.36", + "@types/node": "^20.0.0", + "@types/qrcode": "^1.5.5", "@types/vscode": "^1.80.0", + "@types/ws": "^8.5.13", "typescript": "^5.0.0", "vitest": "^4.0.18" }, @@ -12633,8 +14272,6 @@ }, "packages/vscode-extension/node_modules/@types/node": { "version": "25.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", - "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", "dev": true, "license": "MIT", "dependencies": { @@ -12643,8 +14280,6 @@ }, "packages/vscode-extension/node_modules/undici-types": { "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" } diff --git a/package.json b/package.json index 7d90700..7ef44c7 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,28 @@ { "name": "codelink", - "version": "0.1.0", + "version": "2.0.0", "private": true, "workspaces": [ - "packages/*" + "packages/protocol", + "packages/vscode-extension", + "packages/mobile" ], "scripts": { "build": "npm run build --workspaces --if-present", - "dev": "npm run dev --workspaces --if-present", - "lint": "eslint . --ext .ts,.tsx", - "lint:fix": "eslint . --ext .ts,.tsx --fix", + "lint": "eslint . --ext .ts,.tsx --ignore-path .eslintignore", + "lint:fix": "eslint . --ext .ts,.tsx --ignore-path .eslintignore --fix", "format": "prettier --write \"**/*.{ts,tsx,json,md}\"", "format:check": "prettier --check \"**/*.{ts,tsx,json,md}\"", - "typecheck": "npm run typecheck:protocol && npm run typecheck:relay && npm run typecheck:vscode", + "typecheck": "npm run typecheck:protocol && npm run typecheck:vscode && npm run typecheck:mobile", "typecheck:protocol": "cd packages/protocol && tsc --noEmit", - "typecheck:relay": "cd packages/relay-server && tsc --noEmit", "typecheck:vscode": "cd packages/vscode-extension && tsc --noEmit", - "typecheck:mobile": "cd packages/mobile-client && tsc --noEmit", - "test": "vitest run", - "test:unit": "vitest run tests/unit", - "test:integration": "vitest run tests/integration", - "test:property": "vitest run tests/property", - "test:performance": "vitest run tests/performance", - "test:watch": "vitest", - "test:coverage": "vitest run --coverage", - "test:ci": "vitest run --coverage --reporter=verbose", - "verify:coverage": "node tests/setup/test-coverage-formats.js", + "typecheck:mobile": "cd packages/mobile && tsc --noEmit", + "test": "npm run test --workspace=packages/protocol", + "test:watch": "npm run test --workspace=packages/protocol -- --watch", + "test:coverage": "npm run test --workspace=packages/protocol -- --coverage", + "test:ci": "npm run test --workspace=packages/protocol -- --coverage --reporter=verbose", + "test:go": "go test ./services/auth/... ./services/relay/...", + "test:go:verbose": "go test -v ./services/auth/... ./services/relay/...", "precommit": "npm run lint && npm run typecheck && npm run format:check" }, "devDependencies": { @@ -34,17 +31,9 @@ "@typescript-eslint/parser": "^6.0.0", "@vitest/coverage-v8": "^4.1.0", "@vitest/ui": "^4.1.0", - "babel-preset-expo": "^54.0.10", "eslint": "^8.0.0", - "fast-check": "^4.6.0", "prettier": "^3.0.0", "typescript": "^5.0.0", "vitest": "^4.1.0" - }, - "overrides": { - "react": "19.1.0", - "react-dom": "19.1.0", - "@types/react": "19.0.12", - "@types/react-dom": "19.0.2" } } diff --git a/packages/mobile-client/.npmrc b/packages/mobile-client/.npmrc deleted file mode 100644 index 521a9f7..0000000 --- a/packages/mobile-client/.npmrc +++ /dev/null @@ -1 +0,0 @@ -legacy-peer-deps=true diff --git a/packages/mobile-client/App.tsx b/packages/mobile-client/App.tsx deleted file mode 100644 index c9f607a..0000000 --- a/packages/mobile-client/App.tsx +++ /dev/null @@ -1,334 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, View } from 'react-native'; -import { SafeAreaProvider } from 'react-native-safe-area-context'; -import { Provider as PaperProvider, BottomNavigation, Badge } from 'react-native-paper'; -import { MaterialCommunityIcons } from '@expo/vector-icons'; -import type { InjectPromptResponse } from '@codelink/protocol'; - -// Components -import { - Dashboard, - PromptComposer, - PromptResponseDisplay, - DiffViewer, - ErrorBoundary, - EmptyState, - Settings, - AppLoading, -} from './src/components'; - -// Services -import { PromptManagerImpl, DiffMessageHandler } from './src/services'; -import type { DiffState } from './src/services'; - -// Hooks -import { - ConnectionStatusProvider, - useConnection, - ThemeProvider, - useTheme, - usePromptHistory, - useScreenReaderAnnouncement, -} from './src/hooks'; - -// Config -import { getConfig } from './src/config'; - -// Design System -import { useCustomFonts } from './src/design-system/typography/fontLoading'; - -// Utils -import { isInjectPromptResponse, isSyncFullContextMessage } from './src/utils/messageValidation'; -import { - getStatusBarStyle, - registerBackButtonHandler, - isAndroid, - triggerHapticFeedback, -} from './src/utils/platformAdaptations'; - -/** - * Main application content with navigation - */ -const AppContent: React.FC = () => { - const { status, error, socketManager, reconnect } = useConnection(); - const { theme, isDark } = useTheme(); - const { updateHistoryItem } = usePromptHistory(); - const { announce } = useScreenReaderAnnouncement(); - const [index, setIndex] = useState(0); - const [promptResponse, setPromptResponse] = useState(null); - const [isSubmitting, setIsSubmitting] = useState(false); - const [promptError, setPromptError] = useState(null); - const [currentPromptId, setCurrentPromptId] = useState(null); - const [diffState, setDiffState] = useState({ - currentDiff: null, - history: [], - selectedIndex: -1, - }); - - // Initialize PromptManager and DiffMessageHandler - const [promptManager] = useState(() => new PromptManagerImpl(socketManager)); - const [diffHandler] = useState(() => new DiffMessageHandler(50)); - - // Set up message routing from SocketManager to handlers - useEffect(() => { - // Handle incoming messages - const messageHandler = (message: unknown) => { - // Route INJECT_PROMPT_RESPONSE to PromptManager - if (isInjectPromptResponse(message)) { - promptManager.handleResponse(message); - } - - // Route SYNC_FULL_CONTEXT to DiffMessageHandler - if (isSyncFullContextMessage(message)) { - diffHandler.handleMessage(message); - } - }; - - socketManager.onMessage(messageHandler); - - // Set up PromptManager response callback - promptManager.onResponse((response) => { - setPromptResponse(response); - setIsSubmitting(false); - - // Update history with result - if (currentPromptId) { - updateHistoryItem(currentPromptId, { - success: response.payload.success, - editorUsed: response.payload.editorUsed, - }); - } - - // Haptic feedback (Requirements 22.3, 22.4) - if (response.payload.success) { - triggerHapticFeedback('success'); - } else { - triggerHapticFeedback('error'); - } - - // Clear response after 4 seconds - setTimeout(() => { - setPromptResponse(null); - }, 4000); - }); - - // Set up DiffMessageHandler state change listener - diffHandler.onStateChange((state) => { - setDiffState(state); - }); - - // Set up DiffMessageHandler error listener - diffHandler.onError((error) => { - console.error('Diff handler error:', error); - }); - }, [socketManager, promptManager, diffHandler, currentPromptId, updateHistoryItem]); - - // Handle prompt submission - const handlePromptSubmit = (prompt: string) => { - try { - setIsSubmitting(true); - setPromptError(null); - const promptId = Date.now().toString(); - setCurrentPromptId(promptId); - promptManager.submitPrompt(prompt); - } catch (error) { - setIsSubmitting(false); - setPromptError(error instanceof Error ? error.message : 'Failed to submit prompt'); - triggerHapticFeedback('error'); - } - }; - - // Handle reconnection (currently unused but kept for future use) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const _handleReconnect = async () => { - await triggerHapticFeedback('medium'); - reconnect(); - }; - - // Handle Android hardware back button - // Requirement 26.7: Android hardware back button support - useEffect(() => { - if (!isAndroid()) return; - - const backHandler = () => { - // If not on dashboard, go back to dashboard - if (index !== 0) { - setIndex(0); - return true; // Prevent default back behavior - } - // If on dashboard, allow default behavior (exit app) - return false; - }; - - const cleanup = registerBackButtonHandler(backHandler); - return cleanup; - }, [index]); - - // Define navigation routes - const [routes] = useState([ - { - key: 'dashboard', - title: 'Dashboard', - focusedIcon: 'view-dashboard', - unfocusedIcon: 'view-dashboard-outline', - }, - { - key: 'prompt', - title: 'Prompt', - focusedIcon: 'message-text', - unfocusedIcon: 'message-text-outline', - }, - { key: 'diff', title: 'Diff', focusedIcon: 'file-compare', unfocusedIcon: 'file-compare' }, - { key: 'settings', title: 'Settings', focusedIcon: 'cog', unfocusedIcon: 'cog-outline' }, - ]); - - // Announce screen changes for accessibility - // Requirement 14.11: Announce screen changes on navigation - useEffect(() => { - const currentRoute = routes[index]; - if (currentRoute) { - announce(`${currentRoute.title} screen`, 300); - } - }, [index, routes, announce]); - - // Render scene based on route - const DashboardScene: React.FC = () => ( - - setIndex(1)} - onNavigateToDiffs={() => setIndex(2)} - onRefresh={async () => { - await triggerHapticFeedback('light'); - }} - /> - - ); - - const PromptScene: React.FC = () => ( - - - - - ); - - const DiffScene: React.FC = () => ( - - {diffState.currentDiff ? ( - - ) : ( - setIndex(0)} - /> - )} - - ); - - const SettingsScene: React.FC = () => ( - - - - ); - - const renderScene = BottomNavigation.SceneMap({ - dashboard: DashboardScene, - prompt: PromptScene, - diff: DiffScene, - settings: SettingsScene, - }); - - // Render icon with badge - const renderIcon = ({ - route, - focused, - color, - }: { - route: { key: string; focusedIcon: string; unfocusedIcon: string }; - focused: boolean; - color: string; - }) => { - const icon = (focused ? route.focusedIcon : route.unfocusedIcon) as React.ComponentProps< - typeof MaterialCommunityIcons - >['name']; - - // Show badge on diff tab if there are new diffs - if (route.key === 'diff' && diffState.history.length > 0) { - return ( - - - - - ); - } - - return ; - }; - - return ( - - - - - - - ); -}; - -/** - * Root App component with providers - */ -export default function App() { - const config = getConfig(); - - // Load custom fonts - const { fontsLoaded, fontError } = useCustomFonts(); - - // Show loading screen while fonts are loading - if (!fontsLoaded) { - return ; - } - - // Log font loading error but continue with system fonts - if (fontError) { - console.error('Font loading failed, using system fonts:', fontError); - } - - return ( - - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - scene: { - flex: 1, - }, - badge: { - position: 'absolute', - top: 4, - right: 4, - }, -}); diff --git a/packages/mobile-client/app.json b/packages/mobile-client/app.json deleted file mode 100644 index 0755bdf..0000000 --- a/packages/mobile-client/app.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "expo": { - "name": "CodeLink Mobile", - "slug": "codelink-mobile", - "version": "0.1.0", - "orientation": "default", - "icon": "./assets/icon.png", - "userInterfaceStyle": "light", - "newArchEnabled": true, - "splash": { - "image": "./assets/splash-icon.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.codelink.mobile" - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", - "backgroundColor": "#ffffff" - }, - "package": "com.codelink.mobile", - "edgeToEdgeEnabled": true, - "predictiveBackGestureEnabled": false - }, - "web": { - "favicon": "./assets/favicon.png" - }, - "extra": { - "relayServerUrl": "ws://localhost:3000" - } - } -} diff --git a/packages/mobile-client/assets/adaptive-icon.png b/packages/mobile-client/assets/adaptive-icon.png deleted file mode 100644 index 03d6f6b..0000000 Binary files a/packages/mobile-client/assets/adaptive-icon.png and /dev/null differ diff --git a/packages/mobile-client/assets/favicon.png b/packages/mobile-client/assets/favicon.png deleted file mode 100644 index e75f697..0000000 Binary files a/packages/mobile-client/assets/favicon.png and /dev/null differ diff --git a/packages/mobile-client/assets/icon.png b/packages/mobile-client/assets/icon.png deleted file mode 100644 index a0b1526..0000000 Binary files a/packages/mobile-client/assets/icon.png and /dev/null differ diff --git a/packages/mobile-client/babel.config.js b/packages/mobile-client/babel.config.js deleted file mode 100644 index 3023d5a..0000000 --- a/packages/mobile-client/babel.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - env: { - production: { - plugins: ['react-native-paper/babel'], - }, - }, - }; -}; diff --git a/packages/mobile-client/fix-android-build.sh b/packages/mobile-client/fix-android-build.sh deleted file mode 100755 index a3c69b2..0000000 --- a/packages/mobile-client/fix-android-build.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# Fix Android Build Issues -# This script cleans caches and reinstalls dependencies to fix React hooks errors - -set -e - -echo "======================================" -echo "Fixing Android Build Issues" -echo "======================================" -echo "" - -# Navigate to mobile-client directory -cd "$(dirname "$0")" - -echo "Step 1: Stopping any running Metro bundler..." -pkill -f "react-native" || true -pkill -f "expo" || true -echo "βœ“ Stopped" -echo "" - -echo "Step 2: Cleaning Expo cache..." -rm -rf .expo -echo "βœ“ Expo cache cleared" -echo "" - -echo "Step 3: Cleaning node_modules..." -rm -rf node_modules -echo "βœ“ node_modules removed" -echo "" - -echo "Step 4: Cleaning package-lock.json..." -rm -f package-lock.json -echo "βœ“ package-lock.json removed" -echo "" - -echo "Step 5: Installing dependencies..." -npm install -echo "βœ“ Dependencies installed" -echo "" - -echo "Step 6: Clearing Metro bundler cache..." -npx expo start --clear -echo "" - -echo "======================================" -echo "Build fix complete!" -echo "======================================" -echo "" -echo "Now run: npm run android" diff --git a/packages/mobile-client/index.ts b/packages/mobile-client/index.ts deleted file mode 100644 index 1d6e981..0000000 --- a/packages/mobile-client/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerRootComponent } from 'expo'; - -import App from './App'; - -// registerRootComponent calls AppRegistry.registerComponent('main', () => App); -// It also ensures that whether you load the app in Expo Go or in a native build, -// the environment is set up appropriately -registerRootComponent(App); diff --git a/packages/mobile-client/metro.config.js b/packages/mobile-client/metro.config.js deleted file mode 100644 index 0a13a92..0000000 --- a/packages/mobile-client/metro.config.js +++ /dev/null @@ -1,35 +0,0 @@ -const { getDefaultConfig } = require('expo/metro-config'); -const path = require('path'); - -const projectRoot = __dirname; -const workspaceRoot = path.resolve(projectRoot, '../..'); - -const config = getDefaultConfig(projectRoot); - -// 1. Watch all files within the monorepo -config.watchFolders = [workspaceRoot]; - -// 2. Let Metro know where to resolve packages and in what order -config.resolver.nodeModulesPaths = [ - path.resolve(projectRoot, 'node_modules'), - path.resolve(workspaceRoot, 'node_modules'), -]; - -// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` -config.resolver.disableHierarchicalLookup = true; - -// 4. Disable HMR for web to avoid errors -config.server = { - ...config.server, - enhanceMiddleware: (middleware) => { - return (req, res, next) => { - // Disable HMR for web platform - if (req.url && req.url.includes('platform=web')) { - req.url = req.url.replace(/&hot=true/g, '&hot=false'); - } - return middleware(req, res, next); - }; - }, -}; - -module.exports = config; diff --git a/packages/mobile-client/package.json b/packages/mobile-client/package.json deleted file mode 100644 index f0aaa41..0000000 --- a/packages/mobile-client/package.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "@codelink/mobile-client", - "version": "0.1.0", - "main": "index.ts", - "scripts": { - "start": "expo start", - "android": "expo start --android", - "ios": "expo start --ios", - "web": "expo start --web", - "test": "vitest --run", - "test:watch": "vitest", - "clean": "rm -rf node_modules .expo && npm install" - }, - "dependencies": { - "@callstack/react-theme-provider": "^3.0.9", - "@codelink/protocol": "*", - "@expo-google-fonts/inter": "^0.2.3", - "@expo-google-fonts/manrope": "^0.2.3", - "@expo-google-fonts/space-grotesk": "^0.2.3", - "@expo/vector-icons": "^15.0.3", - "@react-native-async-storage/async-storage": "^2.1.0", - "@react-navigation/bottom-tabs": "^7.15.9", - "@react-navigation/native": "^7.2.2", - "@types/react-syntax-highlighter": "^15.5.13", - "color": "^3.2.1", - "expo": "~54.0.33", - "expo-asset": "^12.0.12", - "expo-blur": "~15.0.8", - "expo-constants": "^18.0.13", - "expo-font": "~12.0.10", - "expo-haptics": "~15.0.8", - "expo-linear-gradient": "~13.0.2", - "expo-modules-core": "^3.0.29", - "expo-splash-screen": "^55.0.14-canary-20260328-2049187", - "expo-status-bar": "~3.0.9", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-native": "0.81.5", - "react-native-paper": "^5.14.5", - "react-native-safe-area-context": "^5.6.2", - "react-native-screens": "^4.24.0", - "react-native-web": "^0.21.0", - "react-syntax-highlighter": "^16.1.1", - "socket.io-client": "^4.8.1" - }, - "devDependencies": { - "@testing-library/react-native": "^13.3.3", - "@types/react": "~19.0.12", - "babel-plugin-react-native-web": "^0.19.12", - "babel-preset-expo": "^54.0.10", - "fast-check": "^3.15.0", - "jsdom": "^25.0.1", - "react-test-renderer": "19.1.0", - "typescript": "~5.9.2", - "vitest": "^4.1.0" - }, - "overrides": { - "react": "19.1.0", - "react-dom": "19.1.0", - "react-test-renderer": "19.1.0" - }, - "private": true -} diff --git a/packages/mobile-client/src/components/AppLoading.tsx b/packages/mobile-client/src/components/AppLoading.tsx deleted file mode 100644 index a0d6d59..0000000 --- a/packages/mobile-client/src/components/AppLoading.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/** - * AppLoading Component - * - * Displays a loading screen while the app initializes (e.g., loading fonts). - * - * Requirements: 2.5, 26.8, 26.9 - */ - -import React from 'react'; -import { View, ActivityIndicator, StyleSheet, Text } from 'react-native'; -import { getActivityIndicatorSize } from '../utils/platformAdaptations'; - -export interface AppLoadingProps { - message?: string; -} - -/** - * Loading screen component - * - * Shows a centered activity indicator with optional message - * while the app is initializing. - * - * Uses platform-specific activity indicator styles: - * - iOS: UIActivityIndicatorView style - * - Android: Material Design CircularProgressIndicator style - */ -export const AppLoading: React.FC = ({ message = 'Loading...' }) => { - return ( - - - {message} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#131313', // surface color from design system - }, - message: { - marginTop: 16, - fontSize: 16, - color: '#e0e0e0', // onSurface color - fontFamily: 'System', - }, -}); diff --git a/packages/mobile-client/src/components/Dashboard.tsx b/packages/mobile-client/src/components/Dashboard.tsx deleted file mode 100644 index e09fdbd..0000000 --- a/packages/mobile-client/src/components/Dashboard.tsx +++ /dev/null @@ -1,620 +0,0 @@ -import React, { useState } from 'react'; -import { View, StyleSheet, ScrollView, FlatList, RefreshControl } from 'react-native'; -import { useDesignSystem } from '../design-system'; -import { Text } from '../design-system/typography/Text'; -import { Card } from '../design-system/components/Card'; -import { Icon } from '../design-system/components/Icon'; -import { Button } from '../design-system/components/Button'; -import { ProgressBar } from '../design-system/components/ProgressBar'; -import { StatusIndicator, ConnectionStatus } from '../design-system/components/StatusIndicator'; -import { TopAppBar } from '../navigation/TopAppBar'; -import { useLoadingAnnouncement } from '../hooks/useScreenReaderAnnouncement'; -import { useResponsiveLayout } from '../hooks/useResponsiveLayout'; - -/** - * System metrics interface - */ -export interface SystemMetrics { - uptime: number; - latency: number; - load: number; - region: string; - trafficSent: number; - trafficReceived: number; -} - -/** - * Activity item interface - */ -export interface ActivityItem { - id: string; - type: 'commit' | 'sync' | 'build' | 'deploy'; - message: string; - timestamp: Date; - metadata?: Record; -} - -/** - * Dashboard component props - */ -export interface DashboardProps { - connectionStatus: ConnectionStatus; - connectionError?: Error | null; - onRetry?: () => void; - metrics?: SystemMetrics; - recentActivity?: ActivityItem[]; - onNavigateToDiffs: () => void; - onNavigateToCompose: () => void; - onRefresh?: () => Promise; -} - -/** - * Dashboard component displays system overview with bento grid layout - * - * Features: - * - TopAppBar with connection status - * - System Overview section with bento grid layout - * - System Health section with status cards - * - Shortcuts section with navigation cards - * - Recent Activity section with activity feed - * - Responsive grid (2 columns on large screens, 1 on small) - * - Asymmetrical spacing and card sizes for bento pattern - * - * Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 13.5, 13.6, 15.3 - */ -export const Dashboard: React.FC = ({ - connectionStatus, - connectionError = null, - onRetry, - metrics = { - uptime: 99.8, - latency: 45, - load: 0.65, - region: 'us-west-2', - trafficSent: 2.5, - trafficReceived: 8.3, - }, - recentActivity = [], - onNavigateToDiffs, - onNavigateToCompose, - onRefresh, -}) => { - const { theme } = useDesignSystem(); - const [refreshing, setRefreshing] = useState(false); - - // Get responsive layout configuration - // Requirements 13.1, 13.2, 13.5, 13.6, 13.9 - const layout = useResponsiveLayout(); - - // Announce loading state for accessibility - // Requirement 14.11: Announce loading states - useLoadingAnnouncement(refreshing, 'Refreshing dashboard data', 'Dashboard refreshed'); - - /** - * Handle refresh (Requirement 3.1) - */ - const handleRefresh = async () => { - if (!onRefresh) return; - setRefreshing(true); - try { - await onRefresh(); - } finally { - setRefreshing(false); - } - }; - - /** - * Format uptime percentage (Requirement 3.3) - */ - const formatUptime = (uptime: number): string => { - return `${uptime.toFixed(1)}%`; - }; - - /** - * Format traffic volume (Requirement 3.4) - */ - const formatTraffic = (traffic: number): string => { - return `${traffic.toFixed(1)} GB`; - }; - - /** - * Format activity timestamp (Requirement 3.7) - */ - const formatActivityTime = (date: Date): string => { - const now = new Date(); - const diff = now.getTime() - date.getTime(); - const minutes = Math.floor(diff / 60000); - if (minutes < 1) return 'Just now'; - if (minutes < 60) return `${minutes}m ago`; - const hours = Math.floor(minutes / 60); - if (hours < 24) return `${hours}h ago`; - return `${Math.floor(hours / 24)}d ago`; - }; - - /** - * Get activity type icon (Requirement 3.7) - */ - const getActivityIcon = (type: string): string => { - switch (type) { - case 'commit': - return 'check-circle'; - case 'sync': - return 'sync'; - case 'build': - return 'build'; - case 'deploy': - return 'cloud-upload'; - default: - return 'info'; - } - }; - - /** - * Render activity feed item (Requirement 3.7, 15.3) - */ - const renderActivityItem = ({ item }: { item: ActivityItem }) => ( - - - - - {item.message} - - - {formatActivityTime(item.timestamp)} - - - - ); - - return ( - - {/* Top App Bar (Requirement 3.1) */} - - - {/* Connection Error Banner (Requirement 17.3, 17.9) */} - {connectionError && connectionStatus === 'disconnected' && ( - - - - - Connection Failed - - - {connectionError.message || 'Unable to connect to relay server'} - - - {onRetry && ( - - )} - - )} - - {/* Scrollable Content */} - - } - > - {/* System Overview Section (Requirement 3.2) */} - - - System Overview - - - {/* Bento Grid Layout (Requirement 3.8, 13.6) */} - - {/* Large card: Uptime (Requirement 3.3) */} - - - Uptime - - - {formatUptime(metrics.uptime)} - - - System availability - - - - {/* Small cards: Latency and Load (Requirement 3.3) */} - - - - Latency - - - {metrics.latency}ms - - - - - - Load - - - {(metrics.load * 100).toFixed(0)}% - - - - - - {/* Traffic Volume Section (Requirement 3.4, 3.9) */} - - - Traffic Volume - - - - - Sent - - - - {formatTraffic(metrics.trafficSent)} - - - - - Received - - - - {formatTraffic(metrics.trafficReceived)} - - - - - - - {/* System Health Section (Requirement 3.5) */} - - - System Health - - - - - - - - - Relay Server - - - {connectionStatus === 'connected' - ? 'Connected' - : connectionStatus === 'connecting' - ? 'Connecting' - : 'Disconnected'} - - - - - - - - - - - API - - - Connected - - - - - - - - {/* Shortcuts Section (Requirement 3.6) */} - - - Shortcuts - - - - - - - Diff Viewer - - - Review code changes - - - - - - - Compose Prompt - - - Send AI prompt - - - - - - {/* Recent Activity Section (Requirement 3.7, 15.3, 23.1) */} - - - Recent Activity - - - {recentActivity.length > 0 ? ( - - item.id} - scrollEnabled={false} - ItemSeparatorComponent={() => ( - - )} - /> - - ) : ( - - - - No Recent Activity - - - Your recent commits, syncs, and deployments will appear here - - - )} - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - errorBanner: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 16, - paddingVertical: 12, - gap: 12, - borderBottomWidth: 1, - }, - errorContent: { - flex: 1, - gap: 4, - }, - errorMessage: { - marginTop: 2, - }, - scrollView: { - flex: 1, - }, - scrollContent: { - paddingHorizontal: 16, - paddingVertical: 16, - paddingBottom: 100, // Space for bottom nav - }, - section: { - marginBottom: 32, - }, - sectionTitle: { - marginBottom: 16, - }, - bentoGrid: { - flexDirection: 'column', - gap: 12, - }, - bentoGridLarge: { - flexDirection: 'row', - }, - bentoCard: { - flex: 1, - }, - bentoCardLarge: { - flex: 2, - }, - bentoColumn: { - gap: 12, - }, - bentoColumnSmall: { - flex: 1, - }, - cardLabel: { - marginBottom: 8, - }, - cardDescription: { - marginTop: 8, - }, - largeMetric: { - marginVertical: 8, - }, - mediumMetric: { - marginVertical: 4, - }, - trafficCard: { - marginTop: 12, - }, - trafficRow: { - flexDirection: 'row', - gap: 16, - marginTop: 12, - }, - trafficColumn: { - flex: 1, - }, - trafficLabel: { - marginBottom: 8, - }, - trafficBar: { - marginVertical: 8, - }, - trafficValue: { - marginTop: 4, - }, - healthGrid: { - flexDirection: 'row', - gap: 12, - }, - healthCard: { - flex: 1, - }, - healthCardContent: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - }, - healthCardText: { - flex: 1, - }, - shortcutsGrid: { - flexDirection: 'row', - gap: 12, - }, - shortcutCard: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - minHeight: 140, - }, - shortcutIcon: { - marginBottom: 12, - }, - shortcutTitle: { - marginBottom: 4, - textAlign: 'center', - }, - activityCard: { - minHeight: 200, - }, - emptyActivityCard: { - alignItems: 'center', - justifyContent: 'center', - paddingVertical: 48, - }, - emptyStateHeadline: { - marginTop: 16, - }, - emptyStateDescription: { - marginTop: 8, - maxWidth: 300, - }, - activityItem: { - flexDirection: 'row', - alignItems: 'flex-start', - gap: 12, - paddingVertical: 8, - }, - activityIcon: { - marginTop: 2, - }, - activityContent: { - flex: 1, - }, - activityTime: { - marginTop: 4, - }, - activityDivider: { - height: 1, - marginVertical: 8, - }, -}); diff --git a/packages/mobile-client/src/components/DiffViewer.tsx b/packages/mobile-client/src/components/DiffViewer.tsx deleted file mode 100644 index f5f5c02..0000000 --- a/packages/mobile-client/src/components/DiffViewer.tsx +++ /dev/null @@ -1,836 +0,0 @@ -import React, { useState, useMemo } from 'react'; -import { View, StyleSheet, ScrollView, RefreshControl, FlatList } from 'react-native'; -import { FileContextPayload } from '@codelink/protocol'; -import { useDesignSystem } from '../design-system'; -import { Text } from '../design-system/typography/Text'; -import { TopAppBar } from '../navigation/TopAppBar'; -import { Card } from '../design-system/components/Card'; -import { Button } from '../design-system/components/Button'; -import { Icon } from '../design-system/components/Icon'; -import { Skeleton } from '../design-system/components/Skeleton'; -import SyntaxHighlighter from 'react-syntax-highlighter'; - -/** - * DiffViewer component props - */ -export interface DiffViewerProps { - payload: FileContextPayload | null; - isLoading?: boolean; - onBack?: () => void; - onRefresh?: () => Promise; - onCommit?: () => void; - onRevert?: () => void; - connectionStatus?: 'connected' | 'disconnected' | 'connecting'; -} - -/** - * Represents a single line in the diff - */ -interface DiffLine { - type: 'addition' | 'deletion' | 'unchanged'; - lineNumber: number; - content: string; - oldLineNumber?: number; - newLineNumber?: number; -} - -/** - * DiffViewer component displays unified file diffs in React Native - * Supports both portrait and landscape orientations with responsive layout - * Provides horizontal and vertical scrolling for long content - * - * Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 4.10, 4.11, 4.12, 4.13, 4.14, - * 15.9, 19.1, 19.2, 19.3, 19.4, 19.5, 19.6, 21.1, 21.2, 21.3, 21.4, 21.5, 21.6, 21.7, 21.8 - */ -export const DiffViewer: React.FC = ({ - payload, - isLoading = false, - onRefresh, - onCommit, - onRevert, - connectionStatus = 'connected', -}) => { - const { theme } = useDesignSystem(); - const [refreshing, setRefreshing] = useState(false); - - // Handle pull-to-refresh - const handleRefresh = async () => { - if (onRefresh) { - setRefreshing(true); - try { - await onRefresh(); - } finally { - setRefreshing(false); - } - } - }; - - // If no payload, show empty state with pull-to-refresh hint - if (!payload) { - return ( - - - - } - accessible={true} - accessibilityLabel="Diff viewer empty state" - accessibilityHint="Pull down to refresh and load diff changes" - > - - - Pull to refresh changes - - - - ); - } - - const { fileName, originalFile, modifiedFile } = payload; - - // Check if this is a new file (no original content) - const isNewFile = originalFile === ''; - - // Check if there are no changes - const noChanges = originalFile === modifiedFile; - - // Detect language from file extension - const detectLanguage = (filename: string): string => { - const ext = filename.split('.').pop()?.toLowerCase(); - const languageMap: Record = { - py: 'python', - js: 'javascript', - jsx: 'javascript', - ts: 'typescript', - tsx: 'typescript', - java: 'java', - c: 'c', - cpp: 'cpp', - cc: 'cpp', - cxx: 'cpp', - go: 'go', - rs: 'rust', - }; - return languageMap[ext || ''] || 'text'; - }; - - const language = detectLanguage(fileName); - - /** - * Create custom syntax highlighting theme matching design system - * Requirements: 21.4, 21.5, 21.6 - */ - const createCustomTheme = (): { [key: string]: React.CSSProperties } => { - return { - hljs: { - display: 'block', - overflowX: 'auto' as const, - background: 'transparent', - color: theme.colors.onSurface, - }, - 'hljs-keyword': { color: theme.colors.primaryContainer }, // Keywords in primaryContainer - 'hljs-built_in': { color: theme.colors.primaryContainer }, - 'hljs-type': { color: theme.colors.primaryContainer }, - 'hljs-literal': { color: theme.colors.primaryContainer }, - 'hljs-string': { color: theme.colors.secondary }, // Strings in secondary - 'hljs-regexp': { color: theme.colors.secondary }, - 'hljs-number': { color: theme.colors.tertiary }, // Numbers in tertiary - 'hljs-comment': { color: theme.colors.onSurfaceVariant }, // Comments in onSurfaceVariant - 'hljs-doctag': { color: theme.colors.onSurfaceVariant }, - 'hljs-function': { color: theme.colors.onSurface }, - 'hljs-title': { color: theme.colors.onSurface }, - 'hljs-params': { color: theme.colors.onSurface }, - 'hljs-variable': { color: theme.colors.onSurface }, - 'hljs-attr': { color: theme.colors.onSurface }, - 'hljs-name': { color: theme.colors.onSurface }, - 'hljs-tag': { color: theme.colors.primaryContainer }, - 'hljs-selector-tag': { color: theme.colors.primaryContainer }, - 'hljs-selector-id': { color: theme.colors.primaryContainer }, - 'hljs-selector-class': { color: theme.colors.primaryContainer }, - 'hljs-meta': { color: theme.colors.onSurfaceVariant }, - 'hljs-meta-keyword': { color: theme.colors.primaryContainer }, - 'hljs-meta-string': { color: theme.colors.secondary }, - }; - }; - - const customTheme = createCustomTheme(); - - /** - * Map file extensions to syntax highlighter language names - * Requirements: 21.3 - */ - const getSyntaxLanguage = (detectedLang: string): string => { - const languageMap: Record = { - python: 'python', - javascript: 'javascript', - typescript: 'typescript', - java: 'java', - c: 'c', - cpp: 'cpp', - go: 'go', - rust: 'rust', - text: 'plaintext', - }; - return languageMap[detectedLang] || 'plaintext'; - }; - - const syntaxLanguage = getSyntaxLanguage(language); - - // Note: Syntax highlighting with react-syntax-highlighter doesn't work well in React Native - // We use monospace font for all code content as a fallback (satisfies requirement 21.8) - // This provides a clean, readable code display that works across all platforms - - /** - * Calculate additions and deletions statistics - */ - const calculateStats = () => { - if (noChanges) return { additions: 0, deletions: 0 }; - - const oldLines = originalFile.split('\n'); - const newLines = modifiedFile.split('\n'); - - let additions = 0; - let deletions = 0; - - // Simple line-based diff calculation - const maxLines = Math.max(oldLines.length, newLines.length); - for (let i = 0; i < maxLines; i++) { - if (i >= oldLines.length) { - additions++; - } else if (i >= newLines.length) { - deletions++; - } else if (oldLines[i] !== newLines[i]) { - additions++; - deletions++; - } - } - - return { additions, deletions }; - }; - - const stats = calculateStats(); - - /** - * Generate simple line-by-line diff - * Memoized to recalculate only when payload changes - */ - const diffLines = useMemo(() => { - const generateDiff = (): DiffLine[] => { - if (noChanges && !isNewFile) return []; - - const oldLines = originalFile.split('\n'); - const newLines = modifiedFile.split('\n'); - const diff: DiffLine[] = []; - - if (isNewFile) { - // All lines are additions - newLines.forEach((line, idx) => { - diff.push({ - type: 'addition', - lineNumber: idx + 1, - content: line, - newLineNumber: idx + 1, - }); - }); - } else { - // Simple line-by-line comparison - const maxLines = Math.max(oldLines.length, newLines.length); - let oldLineNum = 1; - let newLineNum = 1; - - for (let i = 0; i < maxLines; i++) { - if (i >= oldLines.length) { - // Addition - diff.push({ - type: 'addition', - lineNumber: i + 1, - content: newLines[i], - newLineNumber: newLineNum++, - }); - } else if (i >= newLines.length) { - // Deletion - diff.push({ - type: 'deletion', - lineNumber: i + 1, - content: oldLines[i], - oldLineNumber: oldLineNum++, - }); - } else if (oldLines[i] !== newLines[i]) { - // Changed line - show as removal then addition - diff.push({ - type: 'deletion', - lineNumber: i + 1, - content: oldLines[i], - oldLineNumber: oldLineNum++, - }); - diff.push({ - type: 'addition', - lineNumber: i + 1, - content: newLines[i], - newLineNumber: newLineNum++, - }); - } else { - // Unchanged - diff.push({ - type: 'unchanged', - lineNumber: i + 1, - content: oldLines[i], - oldLineNumber: oldLineNum++, - newLineNumber: newLineNum++, - }); - } - } - } - - return diff; - }; - - return generateDiff(); - }, [originalFile, modifiedFile, noChanges, isNewFile]); - - /** - * Get file path parts for display - */ - const getFilePath = () => { - const parts = fileName.split('/'); - return parts.slice(0, -1).join(' / '); - }; - - /** - * Get file name without path - */ - const getFileName = () => { - const parts = fileName.split('/'); - return parts[parts.length - 1]; - }; - - /** - * Render a single diff line with syntax highlighting - * Requirements: 21.1, 21.2, 21.7, 21.8 - */ - const renderLine = ({ item: line, index }: { item: DiffLine; index: number }) => { - const lineStyle = [ - styles.lineContainer, - { - backgroundColor: - line.type === 'addition' - ? `${theme.colors.secondaryContainer}33` // 20% opacity - : line.type === 'deletion' - ? `${theme.colors.errorContainer}33` // 20% opacity - : 'transparent', - borderLeftWidth: line.type !== 'unchanged' ? 3 : 0, - borderLeftColor: - line.type === 'addition' - ? theme.colors.secondary - : line.type === 'deletion' - ? theme.colors.error - : 'transparent', - }, - ]; - - // Determine if we should apply syntax highlighting - // Fallback to plain text if language cannot be detected (Requirement 21.8) - const shouldHighlight = syntaxLanguage !== 'plaintext' && syntaxLanguage !== 'text'; - - return ( - - {/* Line numbers */} - - - {line.oldLineNumber || ''} - - - {line.newLineNumber || ''} - - - - {/* Diff indicator */} - - {line.type === 'addition' && ( - - + - - )} - {line.type === 'deletion' && ( - - - - - )} - - - {/* Code content with syntax highlighting */} - - {shouldHighlight ? ( - - {line.content || ' '} - - ) : ( - - {line.content || ' '} - - )} - - - ); - }; - - if (isLoading) { - return ( - - - - - {/* File header skeleton */} - - - - - - - - - - {/* Diff content skeleton */} - - - {[...Array(10)].map((_, i) => ( - - - - - ))} - - - - {/* Summary footer skeleton */} - - - - - - - ); - } - - return ( - - {/* Top App Bar */} - - - - } - accessible={true} - accessibilityLabel="Diff viewer content" - accessibilityHint="Pull down to refresh diff changes" - > - {/* File Header */} - - - - {/* Unsaved changes indicator */} - {!noChanges && ( - - )} - - - - {getFileName()} - - - {getFilePath() && ( - - {getFilePath()} - - )} - - Last modified:{' '} - {new Date().toLocaleString()} - - - - {/* Action buttons */} - - {onCommit && ( - - )} - {onRevert && ( - - )} - - - - - {/* Unified Diff Code Block */} - - {noChanges && !isNewFile ? ( - - - - No changes - - - ) : diffLines.length > 1000 ? ( - // Virtualized list for diffs exceeding 1000 lines - `${item.lineNumber}-${index}`} - initialNumToRender={50} - maxToRenderPerBatch={50} - windowSize={10} - removeClippedSubviews={true} - style={styles.virtualizedList} - /> - ) : ( - // Regular rendering for smaller diffs - - - {diffLines.map((line, index) => renderLine({ item: line, index }))} - - - )} - - - {/* Diff Summary Footer */} - - - - - - +{stats.additions} - - - - - -{stats.deletions} - - - - - {language} - - - - - - - {/* Contextual Analysis Card */} - - - - - Contextual Analysis - - - - {stats.additions > 0 && stats.deletions > 0 - ? `Modified ${stats.additions + stats.deletions} lines with ${stats.additions} additions and ${stats.deletions} deletions.` - : stats.additions > 0 - ? `Added ${stats.additions} new lines.` - : stats.deletions > 0 - ? `Removed ${stats.deletions} lines.` - : 'No changes detected.'} - - - - {/* Author Information Card */} - - - - - Author Information - - - - - - AI - - - - - AI Editor - - - Automated code modification - - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - scrollView: { - flex: 1, - }, - diffSkeletonContainer: { - padding: 16, - }, - diffLineSkeletonRow: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 12, - }, - emptyStateContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: 24, - minHeight: 400, - }, - emptyStateText: { - marginTop: 16, - textAlign: 'center', - }, - fileHeader: { - margin: 16, - marginBottom: 8, - }, - fileHeaderTop: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - }, - fileInfo: { - flex: 1, - marginRight: 16, - }, - unsavedIndicator: { - width: 8, - height: 8, - borderRadius: 4, - marginBottom: 8, - }, - fileNameContainer: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 4, - }, - fileName: { - marginLeft: 8, - }, - filePath: { - marginTop: 4, - marginBottom: 8, - }, - timestamp: { - flexDirection: 'row', - alignItems: 'center', - }, - actionButtons: { - flexDirection: 'row', - gap: 8, - }, - revertButton: { - marginLeft: 8, - }, - diffContainer: { - marginHorizontal: 16, - marginBottom: 8, - minHeight: 200, - }, - noChangesContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingVertical: 48, - }, - noChangesText: { - marginTop: 16, - }, - horizontalScrollContent: { - minWidth: '100%', - }, - diffLinesContainer: { - flex: 1, - }, - virtualizedList: { - flex: 1, - }, - lineContainer: { - flexDirection: 'row', - minHeight: 24, - paddingVertical: 2, - }, - lineNumbers: { - flexDirection: 'row', - minWidth: 96, - }, - lineNumber: { - width: 48, - textAlign: 'right', - paddingHorizontal: 8, - }, - lineNumberRight: { - borderRightWidth: 1, - }, - diffIndicator: { - width: 32, - justifyContent: 'center', - alignItems: 'center', - }, - lineContent: { - flex: 1, - paddingHorizontal: 8, - }, - summaryFooter: { - marginHorizontal: 16, - marginBottom: 8, - }, - summaryContent: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - statsContainer: { - flexDirection: 'row', - gap: 8, - alignItems: 'center', - }, - statBadge: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 12, - }, - languageBadge: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 12, - }, - contextualCard: { - marginHorizontal: 16, - marginBottom: 8, - }, - authorCard: { - marginHorizontal: 16, - marginBottom: 16, - }, - cardHeader: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 12, - gap: 8, - }, - cardContent: { - lineHeight: 20, - }, - authorInfo: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - }, - avatar: { - width: 40, - height: 40, - borderRadius: 20, - justifyContent: 'center', - alignItems: 'center', - }, - authorDetails: { - flex: 1, - }, -}); - -export default DiffViewer; diff --git a/packages/mobile-client/src/components/EmptyState.tsx b/packages/mobile-client/src/components/EmptyState.tsx deleted file mode 100644 index 5e1922e..0000000 --- a/packages/mobile-client/src/components/EmptyState.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Empty state component - * Displays helpful messages when no content is available - */ - -import React from 'react'; -import { View, StyleSheet } from 'react-native'; -import { Text, Button } from 'react-native-paper'; -import { MaterialCommunityIcons } from '@expo/vector-icons'; - -export interface EmptyStateProps { - icon: keyof typeof MaterialCommunityIcons.glyphMap; - title: string; - description: string; - actionLabel?: string; - onAction?: () => void; -} - -export const EmptyState: React.FC = ({ - icon, - title, - description, - actionLabel, - onAction, -}) => { - return ( - - - - {title} - - - {description} - - {actionLabel && onAction && ( - - )} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: 32, - }, - title: { - marginTop: 16, - marginBottom: 8, - textAlign: 'center', - fontWeight: '600', - }, - description: { - textAlign: 'center', - color: '#666', - marginBottom: 24, - }, - button: { - marginTop: 8, - }, -}); diff --git a/packages/mobile-client/src/components/ErrorBoundary.tsx b/packages/mobile-client/src/components/ErrorBoundary.tsx deleted file mode 100644 index c4120b4..0000000 --- a/packages/mobile-client/src/components/ErrorBoundary.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/** - * ErrorBoundary Component - * - * Catches JavaScript errors anywhere in the component tree and displays - * a fallback UI with retry option. Logs errors to console for debugging. - * - * Requirements: 17.11 - */ - -import React, { Component, ReactNode } from 'react'; -import { View, StyleSheet, ScrollView } from 'react-native'; -import { Text } from '../design-system/typography/Text'; -import { Button } from '../design-system/components/Button'; -import { Icon } from '../design-system/components/Icon'; -import { Card } from '../design-system/components/Card'; - -interface ErrorBoundaryProps { - children: ReactNode; -} - -interface ErrorBoundaryState { - hasError: boolean; - error: Error | null; - errorInfo: React.ErrorInfo | null; -} - -export class ErrorBoundary extends Component { - constructor(props: ErrorBoundaryProps) { - super(props); - this.state = { - hasError: false, - error: null, - errorInfo: null, - }; - } - - static getDerivedStateFromError(error: Error): Partial { - return { - hasError: true, - error, - }; - } - - componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { - // Log error to console (Requirement 17.11) - console.error('ErrorBoundary caught an error:', { - error: error.message, - stack: error.stack, - componentStack: errorInfo.componentStack, - timestamp: new Date().toISOString(), - }); - - // Store error info in state for display - this.setState({ - errorInfo, - }); - } - - handleReset = (): void => { - this.setState({ - hasError: false, - error: null, - errorInfo: null, - }); - }; - - render(): ReactNode { - if (this.state.hasError) { - return ( - - - - - - - Something went wrong - - - - {this.state.error?.message || 'An unexpected error occurred'} - - - - - Error Details - - - {this.state.error?.stack || 'No stack trace available'} - - - - - - ); - } - - return this.props.children; - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#131313', // surface color - }, - contentContainer: { - flexGrow: 1, - justifyContent: 'center', - alignItems: 'center', - padding: 24, - }, - iconContainer: { - marginBottom: 24, - }, - message: { - marginTop: 16, - marginBottom: 24, - }, - errorCard: { - width: '100%', - marginBottom: 24, - padding: 16, - }, - errorLabel: { - marginBottom: 8, - }, - errorText: { - fontFamily: 'monospace', - }, -}); diff --git a/packages/mobile-client/src/components/PromptComposer.tsx b/packages/mobile-client/src/components/PromptComposer.tsx deleted file mode 100644 index 29de409..0000000 --- a/packages/mobile-client/src/components/PromptComposer.tsx +++ /dev/null @@ -1,669 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { - View, - StyleSheet, - ScrollView, - TextInput as RNTextInput, - TouchableOpacity, - Animated, - KeyboardAvoidingView, - Keyboard, - TouchableWithoutFeedback, -} from 'react-native'; -import { LinearGradient } from 'expo-linear-gradient'; -import * as Haptics from 'expo-haptics'; -import { useDesignSystem } from '../design-system/theme'; -import { Text } from '../design-system/typography'; -import { Icon, IconName } from '../design-system/components/Icon'; -import { TopAppBar } from '../navigation/TopAppBar'; -import { useDraftPrompt } from '../hooks/useDraftPrompt'; -import { usePromptHistory } from '../hooks/usePromptHistory'; -import { useLoadingAnnouncement } from '../hooks/useScreenReaderAnnouncement'; -import { getKeyboardBehavior, getKeyboardVerticalOffset } from '../utils/platformAdaptations'; - -/** - * Prompt template definition - */ -interface PromptTemplate { - id: string; - label: string; - icon: IconName; - iconColor: string; - template: string; -} - -/** - * PromptComposer component props - */ -export interface PromptComposerProps { - onSubmit: (prompt: string) => void; - isLoading: boolean; - error: string | null; - connectionStatus?: 'connected' | 'disconnected' | 'connecting'; -} - -/** - * PromptComposer component - Redesigned with Obsidian UI aesthetic - * Features terminal-like interface, template chips, and floating action button - * - * Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 5.10, 5.11, 5.12, 5.13 - * Requirements: 25.1, 25.8, 12.6 - */ -export const PromptComposer: React.FC = ({ - onSubmit, - isLoading, - error, - connectionStatus = 'connected', -}) => { - const { theme } = useDesignSystem(); - const { draft, setDraft, clearDraft } = useDraftPrompt(); - const { addToHistory } = usePromptHistory(); - const [prompt, setPrompt] = useState(draft); - const [charCount, setCharCount] = useState(draft.length); - const fabScale = useRef(new Animated.Value(1)).current; - - const MAX_CHARS = 2000; - - // Announce loading state for accessibility - // Requirement 14.11: Announce loading states - useLoadingAnnouncement(isLoading, 'Sending prompt to AI editor', 'Prompt sent'); - - // Template chips data - // Requirement 5.2: Template chips (Refactor, Explain, Fix Bug, Write Tests, Documentation) - const templates: PromptTemplate[] = [ - { - id: 'refactor', - label: 'Refactor', - icon: 'build', // MaterialIcons equivalent for refactor/fix - iconColor: theme.colors.primary, - template: 'Refactor the following code to improve readability and maintainability:\n\n', - }, - { - id: 'explain', - label: 'Explain', - icon: 'description', // MaterialIcons icon for documentation/explanation - iconColor: theme.colors.secondary, - template: 'Explain what this code does:\n\n', - }, - { - id: 'fix-bug', - label: 'Fix Bug', - icon: 'bug-report', // MaterialIcons icon for bugs - iconColor: theme.colors.error, - template: 'Fix the bug in this code:\n\n', - }, - { - id: 'write-tests', - label: 'Write Tests', - icon: 'check-circle', // MaterialIcons equivalent for checklist/tests - iconColor: theme.colors.tertiary, - template: 'Write unit tests for this code:\n\n', - }, - { - id: 'documentation', - label: 'Documentation', - icon: 'school', // MaterialIcons equivalent for education/documentation - iconColor: theme.colors.primaryContainer, - template: 'Generate documentation for this code:\n\n', - }, - ]; - - // Sync with draft - useEffect(() => { - setDraft(prompt); - }, [prompt, setDraft]); - - /** - * Handle text input changes - * Requirement 5.4: Multiline textarea with character counter - * Requirement 5.5: Character counter showing current/maximum - */ - const handleTextChange = (text: string) => { - if (text.length <= MAX_CHARS) { - setPrompt(text); - setCharCount(text.length); - } - }; - - /** - * Handle template chip selection - * Requirement 5.11: Insert template text into textarea on tap - */ - const handleSelectTemplate = async (template: PromptTemplate) => { - await Haptics.selectionAsync(); - setPrompt(template.template); - setCharCount(template.template.length); - }; - - /** - * Handle clear button - * Requirement 5.6: Clear button in bottom toolbar - */ - const handleClear = async () => { - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - setPrompt(''); - setCharCount(0); - await clearDraft(); - }; - - /** - * Handle attach button (placeholder for future implementation) - * Requirement 5.6: Attach button in bottom toolbar - */ - const handleAttach = async () => { - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - // TODO: Implement file attachment functionality - }; - - /** - * Handle FAB press animation - * Requirement 12.6: Scale animation on FAB press - */ - const handleFABPressIn = () => { - Animated.spring(fabScale, { - toValue: 0.95, - useNativeDriver: true, - }).start(); - }; - - const handleFABPressOut = () => { - Animated.spring(fabScale, { - toValue: 1, - useNativeDriver: true, - }).start(); - }; - - /** - * Handle prompt submission - * Requirement 5.12: Send prompt action on FAB tap - */ - const handleSubmit = async () => { - if (prompt.trim().length === 0 || isLoading || charCount > MAX_CHARS) { - return; - } - - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); - - // Add to history - await addToHistory({ - id: Date.now().toString(), - prompt, - timestamp: Date.now(), - }); - - onSubmit(prompt); - - // Clear draft after successful submission - await clearDraft(); - setPrompt(''); - setCharCount(0); - }; - - const isAtLimit = charCount >= MAX_CHARS; - const canSubmit = prompt.trim().length > 0 && !isLoading && !isAtLimit; - - // Dismiss keyboard when tapping outside - // Requirement 25.3: Dismiss keyboard when user taps outside input field - const dismissKeyboard = () => { - Keyboard.dismiss(); - }; - - return ( - - - - {/* Requirement 5.1: TopAppBar */} - - - - {/* Requirement 5.1, 5.3: Active Context header with "Compose Prompt" title */} - - - Active Context - - - Compose Prompt - - - - {/* Requirement 5.2, 5.3: Horizontal scrolling template chips container */} - - {templates.map((template) => ( - handleSelectTemplate(template)} - activeOpacity={0.7} - accessible={true} - accessibilityLabel={`${template.label} template`} - accessibilityHint="Double tap to insert template into prompt" - accessibilityRole="button" - > - - - {template.label} - - - ))} - - - {/* Requirement 5.3: Main composer container with terminal-like header (colored dots) */} - - {/* Terminal-like header with colored dots */} - - - - - - - New Instruction - - - - - - - - {/* Requirement 5.4, 5.9: Multiline textarea with surfaceContainerLowest background */} - {/* Requirement 25.1, 25.8: Keyboard handling to keep textarea visible */} - - - - {/* Requirement 5.6: Bottom toolbar with Clear and Attach buttons */} - {/* Requirement 5.5, 5.13: Character counter with error state when limit exceeded */} - - - - - - Clear - - - - - - Attach - - - - - - {charCount} / {MAX_CHARS} - - - - - - - {/* Requirement 5.8: Pro tip hint section with lightbulb icon */} - - - - - - Pro Tip:{' '} - - Mention specific functions or file names to help the AI understand the scope of - your requested changes more accurately. - - - - - {/* Error display */} - {error && ( - - - - {error} - - - )} - - - {/* Requirement 5.7, 5.10: Floating action button (FAB) with send icon */} - {/* Requirement 12.6: Gradient background (primary to primaryContainer) and scale animation */} - - - - {isLoading ? ( - - ) : ( - - )} - - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - scrollView: { - flex: 1, - }, - scrollContent: { - paddingHorizontal: 16, - paddingTop: 80, // Account for TopAppBar - paddingBottom: 120, // Account for FAB and bottom nav - }, - contextArea: { - marginTop: 16, - marginBottom: 32, - }, - contextLabel: { - letterSpacing: 3.2, - marginBottom: 4, - }, - contextTitle: { - letterSpacing: -0.5, - }, - templateChipsContainer: { - marginBottom: 24, - marginHorizontal: -16, - }, - templateChipsContent: { - paddingHorizontal: 16, - gap: 12, - }, - templateChip: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - paddingHorizontal: 16, - paddingVertical: 10, - borderRadius: 12, - borderWidth: 1, - borderColor: 'rgba(64, 71, 79, 0.1)', - }, - templateChipLabel: { - fontSize: 12, - }, - composerContainer: { - borderRadius: 16, - overflow: 'hidden', - borderWidth: 1, - borderColor: 'rgba(64, 71, 79, 0.05)', - shadowColor: '#000', - shadowOffset: { width: 0, height: 20 }, - shadowOpacity: 0.4, - shadowRadius: 40, - elevation: 20, - }, - terminalHeader: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: 16, - paddingVertical: 12, - borderBottomWidth: 1, - borderBottomColor: 'rgba(64, 71, 79, 0.1)', - }, - terminalDots: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - }, - dot: { - width: 10, - height: 10, - borderRadius: 5, - }, - terminalLabel: { - marginLeft: 8, - letterSpacing: 2.4, - }, - textareaContainer: { - padding: 24, - }, - textarea: { - minHeight: 200, - fontSize: 18, - lineHeight: 28, - fontFamily: 'Inter_400Regular', - }, - bottomToolbar: { - marginTop: 16, - paddingTop: 16, - borderTopWidth: 1, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - toolbarButtons: { - flexDirection: 'row', - gap: 16, - }, - toolbarButton: { - flexDirection: 'row', - alignItems: 'center', - gap: 4, - }, - toolbarButtonLabel: { - letterSpacing: 1.4, - }, - charCounter: { - paddingHorizontal: 8, - paddingVertical: 4, - borderRadius: 4, - }, - charCounterText: { - letterSpacing: 1.6, - }, - hintSection: { - marginTop: 24, - flexDirection: 'row', - gap: 16, - padding: 16, - borderRadius: 12, - borderWidth: 1, - }, - hintIcon: { - marginTop: 2, - }, - hintTextContainer: { - flex: 1, - }, - hintText: { - lineHeight: 20, - }, - errorContainer: { - marginTop: 16, - flexDirection: 'row', - alignItems: 'center', - gap: 12, - padding: 16, - borderRadius: 12, - borderWidth: 1, - }, - errorText: { - flex: 1, - }, - fabContainer: { - position: 'absolute', - bottom: 110, // Above bottom nav - right: 24, - zIndex: 50, - }, - fab: { - width: 56, - height: 56, - borderRadius: 16, - justifyContent: 'center', - alignItems: 'center', - shadowColor: '#000', - shadowOffset: { width: 0, height: 20 }, - shadowOpacity: 0.4, - shadowRadius: 40, - elevation: 20, - }, -}); diff --git a/packages/mobile-client/src/components/PromptResponseDisplay.tsx b/packages/mobile-client/src/components/PromptResponseDisplay.tsx deleted file mode 100644 index 859e0e5..0000000 --- a/packages/mobile-client/src/components/PromptResponseDisplay.tsx +++ /dev/null @@ -1,354 +0,0 @@ -import React from 'react'; -import { View, StyleSheet, ScrollView } from 'react-native'; -import { InjectPromptResponse } from '@codelink/protocol'; -import SyntaxHighlighter from 'react-syntax-highlighter'; -import { useDesignSystem } from '../design-system'; -import { Text } from '../design-system/typography/Text'; -import { Card } from '../design-system/components/Card'; -import { Icon } from '../design-system/components/Icon'; -import { TopAppBar } from '../navigation/TopAppBar'; - -/** - * PromptResponseDisplay component props - */ -export interface PromptResponseDisplayProps { - response: InjectPromptResponse | null; - connectionStatus?: 'connected' | 'disconnected' | 'connecting'; -} - -/** - * PromptResponseDisplay component - Redesigned with Obsidian UI aesthetic - * Features success/error feedback cards, detail grid, AI response snippet, and troubleshooting hints - * - * Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 6.10, 6.11, 6.12 - * Requirements: 21.2 (Syntax highlighting in code preview) - * Requirements: 18 (Material Symbols icons) - */ -export const PromptResponseDisplay: React.FC = ({ - response, - connectionStatus = 'connected', -}) => { - const { theme } = useDesignSystem(); - - if (!response) { - return null; - } - - const { success, error, editorUsed } = response.payload; - const timestamp = new Date(); - - /** - * Create custom syntax highlighting theme matching design system - * Requirements: 21.2 - */ - const customTheme = { - 'hljs-keyword': { color: theme.colors.primaryContainer }, - 'hljs-string': { color: theme.colors.secondary }, - 'hljs-number': { color: theme.colors.tertiary }, - 'hljs-comment': { color: theme.colors.onSurfaceVariant }, - 'hljs-function': { color: theme.colors.primary }, - 'hljs-variable': { color: theme.colors.onSurface }, - }; - - // Sample code snippet for demonstration - const codeSnippet = `function processPrompt(input: string) { - // AI is processing your request - return analyzeAndExecute(input); -}`; - - return ( - - {/* Requirement 6.1: TopAppBar */} - - - - {/* Requirement 6.1, 6.11, 6.12: Success/Error feedback card */} - - - - - - {success ? 'Success: Prompt Sent' : 'Error: Prompt Failed'} - - {error && ( - - {error} - - )} - - - - - {/* Requirement 6.2, 6.3, 6.8: Asymmetrical detail grid (3-column + 2-column) */} - - {/* 3-column section: Target AI Editor */} - - - Target AI Editor - - - {editorUsed || 'VS Code'} - - - - {/* 2-column section: Timestamp */} - - - - - - Timestamp - - - {timestamp.toLocaleTimeString()} - - - - - - - {/* Requirement 6.4, 6.5, 6.10: AI response snippet with terminal-like header and code preview */} - - {/* Terminal-like header */} - - - - - - - AI Response Snippet - - - - - {/* Requirement 6.5, 6.10: Code preview with syntax highlighting */} - - - {codeSnippet} - - - - - {/* Requirement 6.6: Processing status message */} - - - - - AI agent is processing your request... - - - - - {/* Requirement 6.7: Troubleshooting hint footer with help icon */} - - - - - - Troubleshooting:{' '} - - If your prompt doesn't appear in the editor, check your connection status and ensure - the relay server is running. - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - scrollView: { - flex: 1, - }, - scrollContent: { - paddingHorizontal: 16, - paddingTop: 80, // Account for TopAppBar - paddingBottom: 120, // Account for bottom nav - }, - feedbackCard: { - marginBottom: 24, - }, - feedbackContent: { - flexDirection: 'row', - alignItems: 'center', - gap: 16, - }, - feedbackTextContainer: { - flex: 1, - }, - errorMessage: { - marginTop: 4, - }, - detailGrid: { - flexDirection: 'row', - gap: 16, - marginBottom: 24, - }, - detailCard: { - minHeight: 80, - }, - col3: { - flex: 3, - }, - col2: { - flex: 2, - }, - detailLabel: { - letterSpacing: 2.4, - marginBottom: 8, - }, - detailValue: { - lineHeight: 24, - }, - timestampContent: { - flexDirection: 'row', - alignItems: 'flex-start', - gap: 8, - }, - timestampText: { - flex: 1, - }, - responseCard: { - marginBottom: 24, - overflow: 'hidden', - }, - terminalHeader: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: 16, - paddingVertical: 12, - borderBottomWidth: 1, - borderBottomColor: 'rgba(64, 71, 79, 0.1)', - }, - terminalDots: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - }, - dot: { - width: 10, - height: 10, - borderRadius: 5, - }, - terminalLabel: { - marginLeft: 8, - letterSpacing: 2.4, - }, - codePreview: { - padding: 16, - }, - statusCard: { - marginBottom: 24, - }, - statusContent: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - }, - statusText: { - flex: 1, - lineHeight: 20, - }, - hintSection: { - flexDirection: 'row', - gap: 16, - padding: 16, - borderRadius: 12, - borderWidth: 1, - }, - hintIcon: { - marginTop: 2, - }, - hintTextContainer: { - flex: 1, - }, - hintText: { - lineHeight: 20, - }, -}); diff --git a/packages/mobile-client/src/components/PromptTemplates.tsx b/packages/mobile-client/src/components/PromptTemplates.tsx deleted file mode 100644 index a566c93..0000000 --- a/packages/mobile-client/src/components/PromptTemplates.tsx +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Prompt templates component - * Provides quick access to common prompt templates - */ - -import React from 'react'; -import { View, StyleSheet, ScrollView } from 'react-native'; -import { Card, Text, Chip } from 'react-native-paper'; - -export interface PromptTemplate { - id: string; - title: string; - description: string; - template: string; - category: string; -} - -const TEMPLATES: PromptTemplate[] = [ - { - id: '1', - title: 'Code Review', - description: 'Request a code review', - template: - 'Please review this code for:\n- Best practices\n- Performance issues\n- Security concerns\n- Code style', - category: 'Review', - }, - { - id: '2', - title: 'Bug Fix', - description: 'Report and fix a bug', - template: - 'I found a bug:\n\nSteps to reproduce:\n1. \n2. \n3. \n\nExpected behavior:\n\nActual behavior:\n\nPlease help fix this issue.', - category: 'Bug', - }, - { - id: '3', - title: 'Refactor', - description: 'Request code refactoring', - template: - 'Please refactor this code to:\n- Improve readability\n- Reduce complexity\n- Follow SOLID principles', - category: 'Refactor', - }, - { - id: '4', - title: 'Add Tests', - description: 'Generate unit tests', - template: - 'Please add comprehensive unit tests for this code, including:\n- Happy path scenarios\n- Edge cases\n- Error handling', - category: 'Testing', - }, - { - id: '5', - title: 'Documentation', - description: 'Add code documentation', - template: - 'Please add detailed documentation including:\n- Function/class descriptions\n- Parameter explanations\n- Return value descriptions\n- Usage examples', - category: 'Docs', - }, - { - id: '6', - title: 'Optimize Performance', - description: 'Improve code performance', - template: - 'Please optimize this code for better performance:\n- Reduce time complexity\n- Minimize memory usage\n- Improve algorithm efficiency', - category: 'Performance', - }, -]; - -export interface PromptTemplatesProps { - onSelectTemplate: (template: string) => void; -} - -export const PromptTemplates: React.FC = ({ onSelectTemplate }) => { - const categories = Array.from(new Set(TEMPLATES.map((t) => t.category))); - - return ( - - - Quick Templates - - - - {categories.map((category) => ( - - {category} - - ))} - - - - {TEMPLATES.map((template) => ( - onSelectTemplate(template.template)} - > - - - {template.title} - {template.category} - - - {template.description} - - - - ))} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - padding: 16, - }, - title: { - marginBottom: 12, - fontWeight: '600', - }, - categories: { - marginBottom: 16, - maxHeight: 40, - }, - categoryChip: { - marginRight: 8, - }, - templates: { - flex: 1, - }, - templateCard: { - marginBottom: 12, - }, - templateHeader: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - marginBottom: 8, - }, - description: { - color: '#666', - }, -}); diff --git a/packages/mobile-client/src/components/ResponsiveContainer.tsx b/packages/mobile-client/src/components/ResponsiveContainer.tsx deleted file mode 100644 index 006a5be..0000000 --- a/packages/mobile-client/src/components/ResponsiveContainer.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Responsive Container Component - * - * Wraps content with responsive width constraints and centering. - * - * Requirements: - * - 13.3: Use maximum content width of 1024px on large screens - * - 13.4: Center content horizontally on wide screens - */ - -import React, { type ReactNode } from 'react'; -import { View, StyleSheet } from 'react-native'; -import { useResponsiveLayout } from '../hooks/useResponsiveLayout'; - -/** - * Props for ResponsiveContainer - */ -export interface ResponsiveContainerProps { - children: ReactNode; - style?: string | undefined; -} - -/** - * Container that constrains content width and centers on large screens - * - * Automatically applies: - * - Maximum width of 1024px on screens wider than that - * - Horizontal centering when content is constrained - * - Full width on smaller screens - */ -export const ResponsiveContainer: React.FC = ({ children, style }) => { - const layout = useResponsiveLayout(); - - return ( - - {children} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); diff --git a/packages/mobile-client/src/components/Settings.tsx b/packages/mobile-client/src/components/Settings.tsx deleted file mode 100644 index 4910e23..0000000 --- a/packages/mobile-client/src/components/Settings.tsx +++ /dev/null @@ -1,592 +0,0 @@ -/** - * Settings Screen Component - * - * Redesigned settings screen with connectivity status, configuration options, - * and app information following the Obsidian UI design system. - * - * Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 7.10, 7.11, 7.12, 7.13 - * Requirements: 20.1, 20.2, 20.3, 20.4, 20.5, 20.6 - */ - -import React, { useState, useEffect } from 'react'; -import { - View, - ScrollView, - StyleSheet, - KeyboardAvoidingView, - Keyboard, - TouchableWithoutFeedback, - Linking, - TouchableOpacity, -} from 'react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { useDesignSystem } from '../design-system/theme/useDesignSystem'; -import { Text } from '../design-system/typography/Text'; -import { Card } from '../design-system/components/Card'; -import { TextInput } from '../design-system/components/TextInput'; -import { Toggle } from '../design-system/components/Toggle'; -import { Icon } from '../design-system/components/Icon'; -import { TopAppBar } from '../navigation/TopAppBar'; -import { useConnection } from '../hooks/useConnection'; -import { useConnectionQuality } from '../hooks/useConnectionQuality'; -import { getKeyboardBehavior, getKeyboardVerticalOffset } from '../utils/platformAdaptations'; - -// AsyncStorage keys -const STORAGE_KEYS = { - RELAY_SERVER_URL: '@codelink/relay_server_url', - DARK_MODE: '@codelink/dark_mode', - HIGH_CONTRAST: '@codelink/high_contrast', - PUSH_NOTIFICATIONS: '@codelink/push_notifications', - SOUND_EFFECTS: '@codelink/sound_effects', -}; - -// App version from package.json -const APP_VERSION = '0.1.0'; - -/** - * Settings screen component - */ -export const Settings: React.FC = () => { - const { theme, config, setConfig } = useDesignSystem(); - const { status } = useConnection(); - const { quality, latency } = useConnectionQuality(); - - // State for settings - const [relayServerUrl, setRelayServerUrl] = useState('http://localhost:8080'); - const [urlError, setUrlError] = useState(undefined); - const [darkMode, setDarkMode] = useState(config.mode === 'dark'); - const [highContrast, setHighContrast] = useState(config.highContrast); - const [pushNotifications, setPushNotifications] = useState(false); - const [soundEffects, setSoundEffects] = useState(true); - - /** - * Load saved preferences on mount - */ - useEffect(() => { - loadPreferences(); - }, []); - - /** - * Load preferences from AsyncStorage - */ - const loadPreferences = async () => { - try { - const [ - savedUrl, - savedDarkMode, - savedHighContrast, - savedPushNotifications, - savedSoundEffects, - ] = await Promise.all([ - AsyncStorage.getItem(STORAGE_KEYS.RELAY_SERVER_URL), - AsyncStorage.getItem(STORAGE_KEYS.DARK_MODE), - AsyncStorage.getItem(STORAGE_KEYS.HIGH_CONTRAST), - AsyncStorage.getItem(STORAGE_KEYS.PUSH_NOTIFICATIONS), - AsyncStorage.getItem(STORAGE_KEYS.SOUND_EFFECTS), - ]); - - if (savedUrl) setRelayServerUrl(savedUrl); - if (savedDarkMode !== null) setDarkMode(savedDarkMode === 'true'); - if (savedHighContrast !== null) setHighContrast(savedHighContrast === 'true'); - if (savedPushNotifications !== null) setPushNotifications(savedPushNotifications === 'true'); - if (savedSoundEffects !== null) setSoundEffects(savedSoundEffects === 'true'); - } catch (error) { - console.error('Failed to load preferences:', error); - } - }; - - /** - * Validate URL format - */ - const validateUrl = (url: string): boolean => { - const urlPattern = /^https?:\/\/.+/; - return urlPattern.test(url); - }; - - /** - * Handle relay server URL change - */ - const handleUrlChange = async (url: string) => { - setRelayServerUrl(url); - - // Validate URL - if (url && !validateUrl(url)) { - const errorMsg = 'Invalid URL format. Must start with http:// or https://'; - setUrlError(errorMsg); - // Log validation errors (Requirement 17.11) - console.error('URL validation failed:', { - error: errorMsg, - providedUrl: url, - timestamp: new Date().toISOString(), - }); - return; - } - - setUrlError(undefined); - - // Save to AsyncStorage - try { - await AsyncStorage.setItem(STORAGE_KEYS.RELAY_SERVER_URL, url); - } catch (error) { - console.error('Failed to save relay server URL:', error); - } - }; - - /** - * Handle dark mode toggle - */ - const handleDarkModeChange = async (enabled: boolean) => { - setDarkMode(enabled); - - // Update theme configuration - setConfig({ - ...config, - mode: enabled ? 'dark' : 'light', - }); - - // Also save to legacy AsyncStorage key for backwards compatibility - try { - await AsyncStorage.setItem(STORAGE_KEYS.DARK_MODE, String(enabled)); - } catch (error) { - console.error('Failed to save dark mode preference:', error); - } - }; - - /** - * Handle high contrast toggle - */ - const handleHighContrastChange = async (enabled: boolean) => { - setHighContrast(enabled); - - // Update theme configuration - setConfig({ - ...config, - highContrast: enabled, - }); - - // Also save to legacy AsyncStorage key for backwards compatibility - try { - await AsyncStorage.setItem(STORAGE_KEYS.HIGH_CONTRAST, String(enabled)); - } catch (error) { - console.error('Failed to save high contrast preference:', error); - } - }; - - /** - * Handle push notifications toggle - */ - const handlePushNotificationsChange = async (enabled: boolean) => { - setPushNotifications(enabled); - try { - await AsyncStorage.setItem(STORAGE_KEYS.PUSH_NOTIFICATIONS, String(enabled)); - } catch (error) { - console.error('Failed to save push notifications preference:', error); - } - }; - - /** - * Handle sound effects toggle - */ - const handleSoundEffectsChange = async (enabled: boolean) => { - setSoundEffects(enabled); - try { - await AsyncStorage.setItem(STORAGE_KEYS.SOUND_EFFECTS, String(enabled)); - } catch (error) { - console.error('Failed to save sound effects preference:', error); - } - }; - - /** - * Open external link - */ - const openLink = (url: string) => { - Linking.openURL(url).catch((err) => console.error('Failed to open URL:', err)); - }; - - /** - * Get connection status label - */ - const getStatusLabel = (): string => { - switch (status) { - case 'connected': - return 'Connected'; - case 'disconnected': - return 'Disconnected'; - case 'connecting': - return 'Connecting'; - } - }; - - /** - * Get quality label - */ - const getQualityLabel = (): string => { - switch (quality) { - case 'excellent': - return 'Excellent'; - case 'good': - return 'Good'; - case 'fair': - return 'Fair'; - case 'poor': - return 'Poor'; - case 'offline': - return 'Offline'; - } - }; - - return ( - - {/* Top App Bar */} - - - - Keyboard.dismiss()}> - - - {/* Header */} - - - Settings - - - Configure your development environment - - - - {/* Connectivity Bento Cards */} - - - {/* Status Card */} - - - Status - - - {getStatusLabel()} - - - - {/* Latency Card */} - - - Latency - - - {latency !== null ? `${latency}ms` : '--'} - - - - {/* Active Instance Card */} - - - Active Instance - - - localhost:8080 - - - - {/* Load Card */} - - - Quality - - - {getQualityLabel()} - - - - - - {/* Infrastructure Section */} - - - Infrastructure - - - - - - - {/* Appearance Section */} - - - Appearance - - - - - - - - - {/* Communication Section */} - - - Communication - - - - - - - - - {/* About Section */} - - - About - - - {/* App Icon and Version */} - - - - - - - CodeLink - - - Version {APP_VERSION} - - - - - {/* Links */} - - openLink('https://github.com/codelink/docs')} - accessible={true} - accessibilityLabel="Open documentation" - accessibilityHint="Opens documentation in browser" - accessibilityRole="link" - > - - - Documentation - - - - openLink('https://github.com/codelink/support')} - accessible={true} - accessibilityLabel="Open support" - accessibilityHint="Opens support page in browser" - accessibilityRole="link" - > - - - Support - - - - openLink('https://github.com/codelink')} - accessible={true} - accessibilityLabel="Open GitHub repository" - accessibilityHint="Opens GitHub repository in browser" - accessibilityRole="link" - > - - - GitHub - - - - - - - {/* Bottom padding for safe area */} - - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - keyboardAvoid: { - flex: 1, - }, - scrollView: { - flex: 1, - }, - scrollContent: { - paddingBottom: 32, - }, - header: { - paddingTop: 24, - paddingBottom: 16, - }, - section: { - marginTop: 24, - }, - bentoGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 12, - }, - bentoCard: { - minHeight: 80, - }, - bentoCardSmall: { - flex: 1, - minWidth: '30%', - }, - bentoCardLarge: { - flex: 2, - minWidth: '60%', - }, - aboutHeader: { - flexDirection: 'row', - alignItems: 'center', - }, - appIcon: { - width: 56, - height: 56, - justifyContent: 'center', - alignItems: 'center', - }, - linkItem: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: 12, - minHeight: 44, - }, -}); diff --git a/packages/mobile-client/src/components/index.ts b/packages/mobile-client/src/components/index.ts deleted file mode 100644 index 15b6b88..0000000 --- a/packages/mobile-client/src/components/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Component exports -// This file will export all React Native components - -export { Dashboard } from './Dashboard'; -export type { DashboardProps, SystemMetrics, ActivityItem } from './Dashboard'; -export { DiffViewer } from './DiffViewer'; -export type { DiffViewerProps } from './DiffViewer'; -export { PromptComposer } from './PromptComposer'; -export type { PromptComposerProps } from './PromptComposer'; -export { PromptResponseDisplay } from './PromptResponseDisplay'; -export type { PromptResponseDisplayProps } from './PromptResponseDisplay'; -export { ErrorBoundary } from './ErrorBoundary'; -export { EmptyState } from './EmptyState'; -export type { EmptyStateProps } from './EmptyState'; -export { Settings } from './Settings'; -export { PromptTemplates } from './PromptTemplates'; -export type { PromptTemplate, PromptTemplatesProps } from './PromptTemplates'; -export { AppLoading } from './AppLoading'; -export type { AppLoadingProps } from './AppLoading'; -export { ResponsiveContainer } from './ResponsiveContainer'; -export type { ResponsiveContainerProps } from './ResponsiveContainer'; diff --git a/packages/mobile-client/src/config/README.md b/packages/mobile-client/src/config/README.md deleted file mode 100644 index 1760d25..0000000 --- a/packages/mobile-client/src/config/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Configuration Module - -This module provides centralized configuration management for the mobile client application. - -## Usage - -### Basic Usage - -```typescript -import { getConfig } from './config'; - -const config = getConfig(); -console.log(config.relayServerUrl); // ws://localhost:3000 -console.log(config.socketOptions.reconnectionAttempts); // 5 -``` - -### Environment Variables - -The relay server URL can be configured via: - -1. **Environment variable** (highest priority): - - ```bash - RELAY_SERVER_URL=ws://production-server:3000 npm start - ``` - -2. **app.json extra field** (medium priority): - - ```json - { - "expo": { - "extra": { - "relayServerUrl": "ws://staging-server:3000" - } - } - } - ``` - -3. **Default value** (lowest priority): - ``` - ws://localhost:3000 - ``` - -### Runtime Configuration Updates - -For testing or dynamic configuration: - -```typescript -import { updateConfig } from './config'; - -updateConfig({ - relayServerUrl: 'ws://test-server:4000', - socketOptions: { - reconnectionAttempts: 10, - }, -}); -``` - -## Configuration Reference - -### `relayServerUrl` - -- **Type**: `string` -- **Default**: `ws://localhost:3000` -- **Description**: WebSocket URL for the relay server -- **Requirements**: 2.1 - -### `socketOptions` - -#### `reconnection` - -- **Type**: `boolean` -- **Default**: `true` -- **Description**: Enable automatic reconnection -- **Requirements**: 8.4 - -#### `reconnectionAttempts` - -- **Type**: `number` -- **Default**: `5` -- **Description**: Maximum number of reconnection attempts -- **Requirements**: 8.4 - -#### `reconnectionDelay` - -- **Type**: `number` -- **Default**: `1000` (ms) -- **Description**: Delay between reconnection attempts - -#### `timeout` - -- **Type**: `number` -- **Default**: `20000` (ms) -- **Description**: Connection timeout - -### `ui` - -#### `maxPromptLength` - -- **Type**: `number` -- **Default**: `5000` -- **Description**: Maximum characters allowed in a prompt - -#### `diffHistoryLimit` - -- **Type**: `number` -- **Default**: `50` -- **Description**: Maximum number of diffs to keep in history - -#### `notificationDuration` - -- **Type**: `number` -- **Default**: `4000` (ms) -- **Description**: Duration to show notifications diff --git a/packages/mobile-client/src/config/index.ts b/packages/mobile-client/src/config/index.ts deleted file mode 100644 index 5b66b11..0000000 --- a/packages/mobile-client/src/config/index.ts +++ /dev/null @@ -1,85 +0,0 @@ -import Constants from 'expo-constants'; - -/** - * Application configuration interface - */ -export interface AppConfig { - relayServerUrl: string; - socketOptions: { - reconnection: boolean; - reconnectionAttempts: number; - reconnectionDelay: number; - timeout: number; - }; - ui: { - maxPromptLength: number; - diffHistoryLimit: number; - notificationDuration: number; - }; -} - -/** - * Get relay server URL from environment or configuration - * Priority: Environment variable > app.json extra > default - */ -const getRelayServerUrl = (): string => { - // Check for environment variable (for development/testing) - if (process.env.RELAY_SERVER_URL) { - return process.env.RELAY_SERVER_URL; - } - - // Check Expo Constants for app.json extra configuration - const expoConfig = Constants.expoConfig; - if (expoConfig?.extra?.relayServerUrl) { - return expoConfig.extra.relayServerUrl; - } - - // Fallback to default - return 'ws://localhost:8080'; -}; - -/** - * Default application configuration - * - * Configuration values: - * - relayServerUrl: WebSocket URL for the relay server - * - socketOptions.reconnection: Enable automatic reconnection - * - socketOptions.reconnectionAttempts: Maximum reconnection attempts (Requirement 8.4) - * - socketOptions.reconnectionDelay: Delay between reconnection attempts in ms - * - socketOptions.timeout: Connection timeout in ms - * - ui.maxPromptLength: Maximum characters allowed in a prompt - * - ui.diffHistoryLimit: Maximum number of diffs to keep in history - * - ui.notificationDuration: Duration to show notifications in ms - */ -export const defaultConfig: AppConfig = { - relayServerUrl: getRelayServerUrl(), - socketOptions: { - reconnection: true, - reconnectionAttempts: 5, - reconnectionDelay: 1000, - timeout: 20000, - }, - ui: { - maxPromptLength: 5000, - diffHistoryLimit: 50, - notificationDuration: 4000, - }, -}; - -/** - * Get the current application configuration - * @returns AppConfig object with all configuration values - */ -export const getConfig = (): AppConfig => { - return defaultConfig; -}; - -/** - * Update configuration at runtime (useful for testing) - * @param updates Partial configuration updates to apply - * @returns Updated AppConfig object - */ -export const updateConfig = (updates: Partial): AppConfig => { - Object.assign(defaultConfig, updates); - return defaultConfig; -}; diff --git a/packages/mobile-client/src/design-system/components/Button.tsx b/packages/mobile-client/src/design-system/components/Button.tsx deleted file mode 100644 index e1dc58c..0000000 --- a/packages/mobile-client/src/design-system/components/Button.tsx +++ /dev/null @@ -1,420 +0,0 @@ -/** - * Button Component - * - * A versatile button component with multiple variants, sizes, and states. - * Supports icons, loading states, haptic feedback, and press animations. - * - * Requirements: 10.1, 12.3, 12.9 - */ - -import React, { useRef } from 'react'; -import { - TouchableOpacity, - View, - Text, - StyleSheet, - Animated, - ActivityIndicator, - StyleProp, - ViewStyle, -} from 'react-native'; -import { LinearGradient } from 'expo-linear-gradient'; -import { useDesignSystem } from '../theme/useDesignSystem'; -import { triggerHapticFeedback } from '../../utils/platformAdaptations'; - -/** - * Button variant types - * - primary: Gradient background (primary to primaryContainer) - * - secondary: Secondary color background - * - tertiary: Transparent with secondary text - * - ghost: Transparent with outline - */ -export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'ghost'; - -/** - * Button size variants - * - sm: Small button (44px height) - meets WCAG 2.1 AA minimum - * - md: Medium button (44px height) - default - * - lg: Large button (56px height) - */ -export type ButtonSize = 'sm' | 'md' | 'lg'; - -/** - * Icon position within button - */ -export type IconPosition = 'left' | 'right'; - -/** - * Haptic feedback intensity - */ -export type HapticFeedback = 'light' | 'medium' | 'heavy'; - -export interface ButtonProps { - /** - * Button variant style - * @default 'primary' - */ - variant?: ButtonVariant; - - /** - * Button size - * @default 'md' - */ - size?: ButtonSize; - - /** - * Icon component to display (Material Symbols or custom) - */ - icon?: React.ReactNode; - - /** - * Position of icon relative to text - * @default 'left' - */ - iconPosition?: IconPosition; - - /** - * Disabled state - * @default false - */ - disabled?: boolean; - - /** - * Loading state - shows spinner and disables interaction - * @default false - */ - loading?: boolean; - - /** - * Make button full width of container - * @default false - */ - fullWidth?: boolean; - - /** - * Press handler - */ - onPress: () => void; - - /** - * Button label text - */ - children: React.ReactNode; - - /** - * Custom style overrides - */ - style?: StyleProp; - - /** - * Haptic feedback intensity on press - * @default 'light' - */ - hapticFeedback?: HapticFeedback; - - /** - * Accessibility label for screen readers - * If not provided, uses children text as label - */ - accessibilityLabel?: string; - - /** - * Accessibility hint for screen readers - * Provides additional context about what happens when button is pressed - */ - accessibilityHint?: string; -} - -/** - * Button component with variants, sizes, and animations - */ -export const Button: React.FC = ({ - variant = 'primary', - size = 'md', - icon, - iconPosition = 'left', - disabled = false, - loading = false, - fullWidth = false, - onPress, - children, - style, - hapticFeedback = 'light', - accessibilityLabel, - accessibilityHint, -}) => { - const { theme } = useDesignSystem(); - const scaleAnim = useRef(new Animated.Value(1)).current; - - // Determine if button is interactive - const isInteractive = !disabled && !loading; - - /** - * Handle press in - start scale animation - */ - const handlePressIn = () => { - if (!isInteractive) return; - - Animated.spring(scaleAnim, { - toValue: 0.95, - useNativeDriver: true, - tension: 300, - friction: 10, - }).start(); - }; - - /** - * Handle press out - reset scale animation - */ - const handlePressOut = () => { - if (!isInteractive) return; - - Animated.spring(scaleAnim, { - toValue: 1, - useNativeDriver: true, - tension: 300, - friction: 10, - }).start(); - }; - - /** - * Handle press - trigger haptic feedback and callback - * Requirements: 22.1, 22.6, 22.7 - */ - const handlePress = async () => { - if (!isInteractive) return; - - // Trigger platform-specific haptic feedback - await triggerHapticFeedback(hapticFeedback); - - onPress(); - }; - - /** - * Get button height based on size - * All sizes meet WCAG 2.1 AA minimum 44x44pt touch target (Requirement 14.8) - */ - const getHeight = (): number => { - switch (size) { - case 'sm': - return 44; // Increased from 32 to meet 44pt minimum touch target - case 'md': - return 44; - case 'lg': - return 56; - } - }; - - /** - * Get horizontal padding based on size - */ - const getPaddingHorizontal = (): number => { - switch (size) { - case 'sm': - return theme.spacing.md; - case 'md': - return theme.spacing.lg; - case 'lg': - return theme.spacing.xl; - } - }; - - /** - * Get font size based on size - */ - const getFontSize = (): number => { - switch (size) { - case 'sm': - return theme.typography.sizes.labelMd; - case 'md': - return theme.typography.sizes.labelLg; - case 'lg': - return theme.typography.sizes.titleSm; - } - }; - - /** - * Get text color based on variant and state - */ - const getTextColor = (): string => { - if (disabled) { - return theme.colors.onSurfaceVariant; - } - - switch (variant) { - case 'primary': - return theme.colors.onPrimary; - case 'secondary': - return theme.colors.onSecondary; - case 'tertiary': - case 'ghost': - return theme.colors.secondary; - } - }; - - /** - * Render button content (icon + text + loading spinner) - */ - const renderContent = () => { - const textColor = getTextColor(); - const fontSize = getFontSize(); - - return ( - - {/* Left icon */} - {icon && iconPosition === 'left' && !loading && {icon}} - - {/* Loading spinner */} - {loading && } - - {/* Button text */} - - {children} - - - {/* Right icon */} - {icon && iconPosition === 'right' && !loading && ( - {icon} - )} - - ); - }; - - /** - * Render button based on variant - */ - const renderButton = () => { - const minHeight = getHeight(); - const paddingHorizontal = getPaddingHorizontal(); - - const baseStyle = [ - styles.button, - { - minHeight, // Use minHeight instead of height to allow growth with larger text (Requirement 14.9) - paddingHorizontal, - paddingVertical: theme.spacing.sm, // Add vertical padding for text scaling - borderRadius: theme.borderRadius.lg, - opacity: disabled ? 0.5 : 1, - }, - fullWidth && styles.fullWidth, - style, - ]; - - // Primary variant with gradient - if (variant === 'primary') { - return ( - - {renderContent()} - - ); - } - - // Secondary variant with solid background - if (variant === 'secondary') { - return ( - - {renderContent()} - - ); - } - - // Tertiary variant with transparent background - if (variant === 'tertiary') { - return {renderContent()}; - } - - // Ghost variant with outline - if (variant === 'ghost') { - return ( - - {renderContent()} - - ); - } - }; - - return ( - - - {renderButton()} - - - ); -}; - -const styles = StyleSheet.create({ - button: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - overflow: 'hidden', - }, - fullWidth: { - width: '100%', - }, - contentContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - text: { - textAlign: 'center', - textTransform: 'uppercase', - letterSpacing: 0.5, - }, - iconLeft: { - marginRight: 8, - }, - iconRight: { - marginLeft: 8, - }, - spinner: { - marginRight: 8, - }, -}); diff --git a/packages/mobile-client/src/design-system/components/Card.tsx b/packages/mobile-client/src/design-system/components/Card.tsx deleted file mode 100644 index 39ee66d..0000000 --- a/packages/mobile-client/src/design-system/components/Card.tsx +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Card Component - * - * A versatile card component with surface hierarchy variants for tonal layering. - * Supports custom padding, border radius, elevation, and optional press interaction. - * - * Requirements: 10.2 - */ - -import React from 'react'; -import { View, TouchableOpacity, StyleSheet, StyleProp, ViewStyle } from 'react-native'; -import { useDesignSystem } from '../theme/useDesignSystem'; -import type { SpacingTokens } from '../tokens/spacing'; -import type { BorderRadiusTokens } from '../tokens/borderRadius'; - -/** - * Card variant types mapping to surface hierarchy - * - lowest: surfaceContainerLowest (code blocks, deepest depth) - * - low: surfaceContainerLow (cards on surface) - * - default: surfaceContainer (standard containers) - * - high: surfaceContainerHigh (elevated elements) - * - highest: surfaceContainerHighest (inputs, inactive states) - */ -export type CardVariant = 'lowest' | 'low' | 'default' | 'high' | 'highest'; - -export interface CardProps { - /** - * Card variant mapping to surface hierarchy - * @default 'default' - */ - variant?: CardVariant; - - /** - * Padding size using spacing tokens - * @default 'lg' - */ - padding?: keyof SpacingTokens; - - /** - * Border radius size using border radius tokens - * @default 'lg' - */ - borderRadius?: keyof BorderRadiusTokens; - - /** - * Elevation level (shadow depth) - * Note: Tonal layering is preferred over elevation in Obsidian design - * @default 0 - */ - elevation?: number; - - /** - * Card content - */ - children: React.ReactNode; - - /** - * Custom style overrides - */ - style?: StyleProp; - - /** - * Optional press handler for interactive cards - * When provided, card becomes touchable with press feedback - */ - onPress?: () => void; - - /** - * Accessibility label for screen readers (only used when onPress is provided) - * Describes what the card represents or what happens when pressed - */ - accessibilityLabel?: string; - - /** - * Accessibility hint for screen readers (only used when onPress is provided) - * Provides additional context about what happens when card is pressed - */ - accessibilityHint?: string; -} - -/** - * Card component with surface hierarchy variants - * Memoized for performance optimization (Requirement 15.6) - */ -export const Card = React.memo( - ({ - variant = 'default', - padding = 'lg', - borderRadius = 'lg', - elevation = 0, - children, - style, - onPress, - accessibilityLabel, - accessibilityHint, - }) => { - const { theme } = useDesignSystem(); - - /** - * Get background color based on variant (surface hierarchy) - */ - const getBackgroundColor = (): string => { - switch (variant) { - case 'lowest': - return theme.colors.surfaceContainerLowest; - case 'low': - return theme.colors.surfaceContainerLow; - case 'default': - return theme.colors.surfaceContainer; - case 'high': - return theme.colors.surfaceContainerHigh; - case 'highest': - return theme.colors.surfaceContainerHighest; - } - }; - - /** - * Get padding value from spacing tokens - */ - const getPadding = (): number => { - return theme.spacing[padding]; - }; - - /** - * Get border radius value from border radius tokens - */ - const getBorderRadius = (): number => { - return theme.borderRadius[borderRadius]; - }; - - /** - * Build card style - */ - const cardStyle: StyleProp = [ - styles.card, - { - backgroundColor: getBackgroundColor(), - padding: getPadding(), - borderRadius: getBorderRadius(), - elevation, - }, - style, - ]; - - /** - * Render card as touchable if onPress is provided - */ - if (onPress) { - return ( - - {children} - - ); - } - - /** - * Render card as static view - */ - return {children}; - }, - // Custom comparison function for better memoization - (prevProps, nextProps) => { - return ( - prevProps.variant === nextProps.variant && - prevProps.padding === nextProps.padding && - prevProps.borderRadius === nextProps.borderRadius && - prevProps.elevation === nextProps.elevation && - prevProps.onPress === nextProps.onPress && - prevProps.style === nextProps.style && - prevProps.children === nextProps.children && - prevProps.accessibilityLabel === nextProps.accessibilityLabel && - prevProps.accessibilityHint === nextProps.accessibilityHint - ); - } -); - -const styles = StyleSheet.create({ - card: { - overflow: 'hidden', - }, -}); diff --git a/packages/mobile-client/src/design-system/components/Chip.tsx b/packages/mobile-client/src/design-system/components/Chip.tsx deleted file mode 100644 index 6d3e783..0000000 --- a/packages/mobile-client/src/design-system/components/Chip.tsx +++ /dev/null @@ -1,351 +0,0 @@ -/** - * Chip Component - * - * A compact, interactive chip component for tags, filters, and selections. - * Supports icons, selected states, variants, and haptic feedback. - * - * Requirements: 10.3, 5.11 - */ - -import React from 'react'; -import { - TouchableOpacity, - View, - Text, - StyleSheet, - StyleProp, - ViewStyle, - Platform, -} from 'react-native'; -import * as Haptics from 'expo-haptics'; -import { useDesignSystem } from '../theme/useDesignSystem'; - -/** - * Chip variant types - * - default: Standard chip with surface colors - * - success: Success state with secondary (green) color - * - error: Error state with error (red) color - * - warning: Warning state with tertiary (orange) color - */ -export type ChipVariant = 'default' | 'success' | 'error' | 'warning'; - -/** - * Chip size variants - * - sm: Small chip (44px height) - meets WCAG 2.1 AA minimum - * - md: Medium chip (44px height) - default, meets WCAG 2.1 AA minimum - */ -export type ChipSize = 'sm' | 'md'; - -export interface ChipProps { - /** - * Chip label text - */ - label: string; - - /** - * Optional icon component to display (Material Symbols or custom) - */ - icon?: React.ReactNode; - - /** - * Selected state - highlights chip with secondary color - * @default false - */ - selected?: boolean; - - /** - * Chip variant for different semantic states - * @default 'default' - */ - variant?: ChipVariant; - - /** - * Chip size - * @default 'md' - */ - size?: ChipSize; - - /** - * Optional press handler for interactive chips - */ - onPress?: () => void; - - /** - * Custom style overrides - */ - style?: StyleProp; - - /** - * Accessibility label for screen readers - * If not provided, uses label text as accessibility label - */ - accessibilityLabel?: string; - - /** - * Accessibility hint for screen readers - * Provides additional context about what happens when chip is pressed - */ - accessibilityHint?: string; -} - -/** - * Chip component for tags, filters, and selections - */ -export const Chip: React.FC = ({ - label, - icon, - selected = false, - variant = 'default', - size = 'md', - onPress, - style, - accessibilityLabel, - accessibilityHint, -}) => { - const { theme } = useDesignSystem(); - - /** - * Handle press - trigger haptic feedback and callback - */ - const handlePress = () => { - if (!onPress) return; - - // Trigger haptic feedback on press - if (Platform.OS === 'ios' || Platform.OS === 'android') { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - } - - onPress(); - }; - - /** - * Get chip height based on size - * All sizes meet WCAG 2.1 AA minimum 44x44pt touch target (Requirement 14.8) - */ - const getHeight = (): number => { - switch (size) { - case 'sm': - return 44; // Increased from 24 to meet 44pt minimum touch target - case 'md': - return 44; // Increased from 32 to meet 44pt minimum touch target - } - }; - - /** - * Get horizontal padding based on size - * Adjusted for 44pt minimum touch target height - */ - const getPaddingHorizontal = (): number => { - switch (size) { - case 'sm': - return theme.spacing.md; // 12px - case 'md': - return theme.spacing.lg; // 16px - } - }; - - /** - * Get font size based on size - */ - const getFontSize = (): number => { - switch (size) { - case 'sm': - return theme.typography.sizes.labelSm; - case 'md': - return theme.typography.sizes.labelMd; - } - }; - - /** - * Get icon size based on chip size - * Adjusted for 44pt minimum touch target height - */ - const getIconSize = (): number => { - switch (size) { - case 'sm': - return 18; // Increased from 14 for better proportion - case 'md': - return 20; // Increased from 16 for better proportion - } - }; - - /** - * Get background color based on variant and selected state - */ - const getBackgroundColor = (): string => { - // Selected state always uses secondary color - if (selected) { - return theme.colors.secondary; - } - - // Variant colors for unselected state - switch (variant) { - case 'default': - return theme.colors.surfaceContainerHigh; - case 'success': - return `${theme.colors.secondary}26`; // 15% opacity - case 'error': - return `${theme.colors.error}26`; // 15% opacity - case 'warning': - return `${theme.colors.tertiary}26`; // 15% opacity - } - }; - - /** - * Get text color based on variant and selected state - */ - const getTextColor = (): string => { - // Selected state uses onSecondary color - if (selected) { - return theme.colors.onSecondary; - } - - // Variant colors for unselected state - switch (variant) { - case 'default': - return theme.colors.onSurface; - case 'success': - return theme.colors.secondary; - case 'error': - return theme.colors.error; - case 'warning': - return theme.colors.tertiary; - } - }; - - /** - * Get border color based on variant and selected state - */ - const getBorderColor = (): string | undefined => { - // Selected state has no border - if (selected) { - return undefined; - } - - // Variant border colors for unselected state - switch (variant) { - case 'default': - return theme.colors.outline; - case 'success': - return theme.colors.secondary; - case 'error': - return theme.colors.error; - case 'warning': - return theme.colors.tertiary; - } - }; - - const minHeight = getHeight(); - const paddingHorizontal = getPaddingHorizontal(); - const fontSize = getFontSize(); - const iconSize = getIconSize(); - const backgroundColor = getBackgroundColor(); - const textColor = getTextColor(); - const borderColor = getBorderColor(); - - const chipStyle: StyleProp = [ - styles.chip, - { - minHeight, // Use minHeight instead of height to allow growth with larger text (Requirement 14.9) - paddingHorizontal, - paddingVertical: theme.spacing.xs, // Add vertical padding for text scaling - backgroundColor, - borderRadius: theme.borderRadius.full, // rounded-full - borderWidth: borderColor ? 1 : 0, - borderColor, - }, - style, - ]; - - /** - * Render chip content (icon + label) - */ - const renderContent = () => ( - - {/* Icon */} - {icon && ( - {icon} - )} - - {/* Label */} - - {label} - - - ); - - /** - * Render chip as touchable if onPress is provided - */ - if (onPress) { - return ( - - {renderContent()} - - ); - } - - /** - * Render chip as static view - */ - return ( - - {renderContent()} - - ); -}; - -const styles = StyleSheet.create({ - chip: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - alignSelf: 'flex-start', // Don't stretch to full width - }, - contentContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - iconContainer: { - marginRight: 4, - alignItems: 'center', - justifyContent: 'center', - }, - label: { - textTransform: 'uppercase', - letterSpacing: 0.5, - }, -}); diff --git a/packages/mobile-client/src/design-system/components/GlassContainer.tsx b/packages/mobile-client/src/design-system/components/GlassContainer.tsx deleted file mode 100644 index 289ba73..0000000 --- a/packages/mobile-client/src/design-system/components/GlassContainer.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Glass Container Component - * - * A container with glassmorphism effect using backdrop blur. - * Provides a frosted glass visual effect for floating elements. - * - * Requirements: - * - 11.1: Apply backdrop-blur effect to Bottom_Navigation bar - * - 11.2: Apply backdrop-blur effect to modal overlays - * - 11.3: Apply backdrop-blur effect to floating action buttons - * - 11.4: Use 20px blur radius for glassmorphism effects - * - 11.5: Use 80-90% opacity for glass surfaces - * - 11.6: Layer glass effects over surface-variant background - * - 11.7: Fallback to solid background when backdrop-blur not supported - */ - -import React, { type ReactNode } from 'react'; -import { View, StyleSheet, Platform, StyleProp, ViewStyle } from 'react-native'; -import { BlurView } from 'expo-blur'; -import { useDesignSystem } from '../theme/useDesignSystem'; - -/** - * Glass container props - */ -export interface GlassContainerProps { - /** - * Child components to render inside the glass container - */ - children: ReactNode; - - /** - * Blur intensity (0-100) - * @default 80 - */ - intensity?: number; - - /** - * Tint color for the blur - * @default 'dark' - */ - tint?: 'light' | 'dark' | 'default'; - - /** - * Background opacity (0-1) - * @default 0.8 - */ - opacity?: number; - - /** - * Custom style overrides - */ - style?: StyleProp; - - /** - * Whether to use fallback solid background on unsupported platforms - * @default true - */ - useFallback?: boolean; -} - -/** - * Glass container component with glassmorphism effect - * - * Uses BlurView for backdrop blur on supported platforms (iOS, Android). - * Falls back to semi-transparent solid background on web or when blur is not supported. - */ -export const GlassContainer: React.FC = ({ - children, - intensity = 80, - tint = 'dark', - opacity = 0.8, - style, - useFallback = true, -}) => { - const { theme } = useDesignSystem(); - - // Check if platform supports blur - const supportsBlur = Platform.OS === 'ios' || Platform.OS === 'android'; - - // Fallback background color with opacity - const fallbackBackgroundColor = `${theme.colors.surfaceVariant}${Math.round(opacity * 255) - .toString(16) - .padStart(2, '0')}`; - - if (!supportsBlur && useFallback) { - // Fallback for platforms without blur support (Requirement 11.7) - return ( - - {children} - - ); - } - - // Glass effect with BlurView (Requirements 11.4, 11.5, 11.6) - return ( - - {/* Backdrop blur layer */} - - - {/* Semi-transparent background overlay */} - - - {/* Content layer */} - {children} - - ); -}; - -const styles = StyleSheet.create({ - container: { - overflow: 'hidden', - }, - content: { - flex: 1, - }, -}); diff --git a/packages/mobile-client/src/design-system/components/Icon.tsx b/packages/mobile-client/src/design-system/components/Icon.tsx deleted file mode 100644 index 3116f9f..0000000 --- a/packages/mobile-client/src/design-system/components/Icon.tsx +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Icon Component - * - * A wrapper component for Material Icons with design system integration. - * Supports customization of size, color, fill, and weight. - * - * Requirements: 10.7, 18.11, 18.12 - */ - -import React from 'react'; -import { StyleProp, TextStyle } from 'react-native'; -import { MaterialIcons } from '@expo/vector-icons'; -import { useDesignSystem } from '../theme/useDesignSystem'; -import { ColorTokens } from '../tokens'; - -/** - * Icon weight variants (Material Icons supports limited weights) - */ -export type IconWeight = 100 | 200 | 300 | 400 | 500 | 600 | 700; - -/** - * Material Icons name type - */ -export type IconName = keyof typeof MaterialIcons.glyphMap; - -export interface IconProps { - /** - * Material Icons icon name - */ - name: IconName; - - /** - * Icon size in pixels - * @default 24 - */ - size?: number; - - /** - * Icon color - can be a design token key or hex color - * @default 'onSurface' - */ - color?: keyof ColorTokens | string; - - /** - * Fill style (Material Icons doesn't support variable fill, but we keep for API consistency) - * @default false - */ - fill?: boolean; - - /** - * Icon weight (Material Icons uses regular weight by default) - * @default 400 - */ - weight?: IconWeight; - - /** - * Custom style overrides - */ - style?: StyleProp; -} - -/** - * Icon component wrapping Material Icons with design system integration - */ -export const Icon: React.FC = ({ - name, - size = 24, - color = 'onSurface', - fill: _fill = false, - weight: _weight = 400, - style, -}) => { - const { theme } = useDesignSystem(); - - /** - * Resolve color from design token or use as-is - */ - const resolveColor = (): string => { - // Check if color is a design token key - if (color in theme.colors) { - return theme.colors[color as keyof ColorTokens]; - } - // Otherwise use as hex color - return color; - }; - - const iconColor = resolveColor(); - - /** - * Note: Material Icons from @expo/vector-icons doesn't support variable fill or weight - * like Material Symbols. These props are included for API consistency and future compatibility. - * For now, they don't affect the rendering but can be used for conditional logic if needed. - */ - - return ; -}; - -/** - * Common icon names used in the app - * This provides type-safe shortcuts for frequently used icons - */ -export const IconNames = { - // Navigation - home: 'home' as IconName, - settings: 'settings' as IconName, - - // Actions - send: 'send' as IconName, - close: 'close' as IconName, - check: 'check' as IconName, - add: 'add' as IconName, - remove: 'remove' as IconName, - edit: 'edit' as IconName, - delete: 'delete' as IconName, - refresh: 'refresh' as IconName, - search: 'search' as IconName, - - // Status - checkCircle: 'check-circle' as IconName, - error: 'error' as IconName, - warning: 'warning' as IconName, - info: 'info' as IconName, - - // Files and folders - folder: 'folder' as IconName, - folderOpen: 'folder-open' as IconName, - insertDriveFile: 'insert-drive-file' as IconName, - - // Code and development - code: 'code' as IconName, - terminal: 'terminal' as IconName, - - // Time - schedule: 'schedule' as IconName, - - // UI elements - arrowBack: 'arrow-back' as IconName, - arrowForward: 'arrow-forward' as IconName, - arrowDropDown: 'arrow-drop-down' as IconName, - arrowDropUp: 'arrow-drop-up' as IconName, - menu: 'menu' as IconName, - moreVert: 'more-vert' as IconName, - moreHoriz: 'more-horiz' as IconName, - - // Media - playArrow: 'play-arrow' as IconName, - pause: 'pause' as IconName, - stop: 'stop' as IconName, - - // Communication - notifications: 'notifications' as IconName, - notificationsOff: 'notifications-off' as IconName, - - // Misc - lightbulb: 'lightbulb' as IconName, - help: 'help' as IconName, - link: 'link' as IconName, - attach: 'attach-file' as IconName, - visibility: 'visibility' as IconName, - visibilityOff: 'visibility-off' as IconName, -} as const; diff --git a/packages/mobile-client/src/design-system/components/ProgressBar.tsx b/packages/mobile-client/src/design-system/components/ProgressBar.tsx deleted file mode 100644 index cf8fc2a..0000000 --- a/packages/mobile-client/src/design-system/components/ProgressBar.tsx +++ /dev/null @@ -1,204 +0,0 @@ -/** - * ProgressBar Component - * - * A horizontal progress bar component for visualizing metrics and progress. - * Supports color variants, percentage display, and optional labels. - * - * Requirements: 10.9, 3.4 - */ - -import React from 'react'; -import { View, Text, StyleSheet, StyleProp, ViewStyle } from 'react-native'; -import { useDesignSystem } from '../theme/useDesignSystem'; - -/** - * Progress bar color variants - */ -export type ProgressBarVariant = 'primary' | 'secondary' | 'tertiary' | 'error'; - -export interface ProgressBarProps { - /** - * Progress percentage (0-100) - */ - progress: number; - - /** - * Color variant - * @default 'primary' - */ - variant?: ProgressBarVariant; - - /** - * Show percentage label - * @default false - */ - showLabel?: boolean; - - /** - * Custom label text (overrides percentage) - */ - label?: string; - - /** - * Progress bar height - * @default 8 - */ - height?: number; - - /** - * Custom style overrides - */ - style?: StyleProp; - - /** - * Accessibility label for screen readers - * If not provided, uses progress percentage as accessibility label - */ - accessibilityLabel?: string; -} - -/** - * ProgressBar component for visualizing metrics - */ -export const ProgressBar: React.FC = ({ - progress, - variant = 'primary', - showLabel = false, - label, - height = 8, - style, - accessibilityLabel, -}) => { - const { theme } = useDesignSystem(); - - // Clamp progress between 0 and 100 - const clampedProgress = Math.max(0, Math.min(100, progress)); - - /** - * Get progress bar color based on variant - */ - const getProgressColor = (): string => { - switch (variant) { - case 'primary': - return theme.colors.primary; - case 'secondary': - return theme.colors.secondary; - case 'tertiary': - return theme.colors.tertiary; - case 'error': - return theme.colors.error; - } - }; - - /** - * Get background color (dimmed version of progress color) - */ - const getBackgroundColor = (): string => { - switch (variant) { - case 'primary': - return theme.colors.primaryContainer; - case 'secondary': - return theme.colors.secondaryContainer; - case 'tertiary': - return theme.colors.tertiaryContainer; - case 'error': - return theme.colors.errorContainer; - } - }; - - const progressColor = getProgressColor(); - const backgroundColor = getBackgroundColor(); - - /** - * Get label text to display - */ - const getLabelText = (): string => { - if (label) { - return label; - } - if (showLabel) { - return `${Math.round(clampedProgress)}%`; - } - return ''; - }; - - const labelText = getLabelText(); - - return ( - - {/* Progress bar */} - - - - - {/* Optional label */} - {labelText && ( - - {labelText} - - )} - - ); -}; - -const styles = StyleSheet.create({ - container: { - width: '100%', - }, - track: { - width: '100%', - overflow: 'hidden', - }, - fill: { - height: '100%', - }, - label: { - marginTop: 4, - textAlign: 'right', - textTransform: 'uppercase', - letterSpacing: 0.5, - }, -}); diff --git a/packages/mobile-client/src/design-system/components/Skeleton.tsx b/packages/mobile-client/src/design-system/components/Skeleton.tsx deleted file mode 100644 index 6494c77..0000000 --- a/packages/mobile-client/src/design-system/components/Skeleton.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Skeleton Component - * - * A loading placeholder component with shimmer animation. - * Used to indicate content is loading. - * - * Requirements: 15.10 - */ - -import React, { useEffect, useRef } from 'react'; -import { View, Animated, StyleSheet, StyleProp, ViewStyle, DimensionValue } from 'react-native'; -import { useDesignSystem } from '../theme/useDesignSystem'; - -export interface SkeletonProps { - /** - * Width of skeleton (number for pixels, string for percentage) - */ - width?: number | string; - - /** - * Height of skeleton (number for pixels, string for percentage) - */ - height?: number | string; - - /** - * Make skeleton circular - * @default false - */ - circle?: boolean; - - /** - * Custom style overrides - */ - style?: StyleProp; -} - -/** - * Skeleton component with shimmer animation - */ -export const Skeleton: React.FC = ({ - width = '100%', - height = 20, - circle = false, - style, -}) => { - const { theme } = useDesignSystem(); - const shimmerAnim = useRef(new Animated.Value(0)).current; - - /** - * Start shimmer animation on mount - */ - useEffect(() => { - const shimmer = Animated.loop( - Animated.sequence([ - Animated.timing(shimmerAnim, { - toValue: 1, - duration: 1500, - useNativeDriver: true, - }), - Animated.timing(shimmerAnim, { - toValue: 0, - duration: 1500, - useNativeDriver: true, - }), - ]) - ); - - shimmer.start(); - - return () => { - shimmer.stop(); - }; - }, [shimmerAnim]); - - const opacity = shimmerAnim.interpolate({ - inputRange: [0, 1], - outputRange: [0.3, 0.7], - }); - - const borderRadius = circle - ? typeof height === 'number' - ? height / 2 - : 9999 - : theme.borderRadius.md; - - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - skeleton: { - overflow: 'hidden', - }, -}); diff --git a/packages/mobile-client/src/design-system/components/StatusIndicator.tsx b/packages/mobile-client/src/design-system/components/StatusIndicator.tsx deleted file mode 100644 index 22e5211..0000000 --- a/packages/mobile-client/src/design-system/components/StatusIndicator.tsx +++ /dev/null @@ -1,237 +0,0 @@ -/** - * StatusIndicator Component - * - * A status indicator component that displays connection status with color-coded dots. - * Supports connected, disconnected, and connecting states with pulse animation. - * - * Requirements: 10.6, 3.10, 3.11, 9.6, 9.7, 9.8 - */ - -import React, { useEffect, useRef } from 'react'; -import { View, Text, StyleSheet, Animated, StyleProp, ViewStyle } from 'react-native'; -import { useDesignSystem } from '../theme/useDesignSystem'; - -/** - * Connection status types - */ -export type ConnectionStatus = 'connected' | 'disconnected' | 'connecting'; - -/** - * Status indicator size variants - */ -export type StatusIndicatorSize = 'sm' | 'md' | 'lg'; - -export interface StatusIndicatorProps { - /** - * Current connection status - */ - status: ConnectionStatus; - - /** - * Show label text next to indicator - * @default false - */ - showLabel?: boolean; - - /** - * Size variant - * @default 'md' - */ - size?: StatusIndicatorSize; - - /** - * Enable pulse animation for connecting state - * @default true - */ - animated?: boolean; - - /** - * Custom style overrides - */ - style?: StyleProp; - - /** - * Accessibility label for screen readers - * If not provided, uses status label as accessibility label - */ - accessibilityLabel?: string; -} - -/** - * StatusIndicator component with color-coded status and pulse animation - */ -export const StatusIndicator: React.FC = ({ - status, - showLabel = false, - size = 'md', - animated = true, - style, - accessibilityLabel, -}) => { - const { theme } = useDesignSystem(); - const pulseScale = useRef(new Animated.Value(1)).current; - const pulseOpacity = useRef(new Animated.Value(1)).current; - - /** - * Start pulse animation for connecting state - */ - useEffect(() => { - if (status === 'connecting' && animated) { - // Create looping pulse animation - const pulseAnimation = Animated.loop( - Animated.parallel([ - Animated.sequence([ - Animated.timing(pulseScale, { - toValue: 1.5, - duration: 1000, - useNativeDriver: true, - }), - Animated.timing(pulseScale, { - toValue: 1, - duration: 1000, - useNativeDriver: true, - }), - ]), - Animated.sequence([ - Animated.timing(pulseOpacity, { - toValue: 0.5, - duration: 1000, - useNativeDriver: true, - }), - Animated.timing(pulseOpacity, { - toValue: 1, - duration: 1000, - useNativeDriver: true, - }), - ]), - ]) - ); - - pulseAnimation.start(); - - // Cleanup animation on unmount or status change - return () => { - pulseAnimation.stop(); - pulseScale.setValue(1); - pulseOpacity.setValue(1); - }; - } else { - // Reset animation values for non-connecting states - pulseScale.setValue(1); - pulseOpacity.setValue(1); - } - }, [status, animated, pulseScale, pulseOpacity]); - - /** - * Get dot size based on size variant - */ - const getDotSize = (): number => { - switch (size) { - case 'sm': - return 8; - case 'md': - return 10; - case 'lg': - return 12; - } - }; - - /** - * Get status color based on connection status - */ - const getStatusColor = (): string => { - switch (status) { - case 'connected': - return theme.colors.secondary; // Green - case 'disconnected': - return theme.colors.error; // Red - case 'connecting': - return theme.colors.tertiary; // Orange - } - }; - - /** - * Get status label text - */ - const getStatusLabel = (): string => { - switch (status) { - case 'connected': - return 'Connected'; - case 'disconnected': - return 'Disconnected'; - case 'connecting': - return 'Connecting'; - } - }; - - const dotSize = getDotSize(); - const statusColor = getStatusColor(); - const statusLabel = getStatusLabel(); - - return ( - - {/* Status dot with optional pulse animation */} - - - - - {/* Optional label */} - {showLabel && ( - - {statusLabel} - - )} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - alignItems: 'center', - }, - dotContainer: { - justifyContent: 'center', - alignItems: 'center', - }, - dot: { - // Size and color set dynamically - }, - label: { - marginLeft: 8, - textTransform: 'uppercase', - letterSpacing: 0.5, - }, -}); diff --git a/packages/mobile-client/src/design-system/components/TextInput.tsx b/packages/mobile-client/src/design-system/components/TextInput.tsx deleted file mode 100644 index 27a8dd7..0000000 --- a/packages/mobile-client/src/design-system/components/TextInput.tsx +++ /dev/null @@ -1,409 +0,0 @@ -/** - * TextInput Component - * - * A versatile text input component with focus states, error handling, - * character counter, and support for multiline input. - * - * Requirements: 10.4, 5.4, 5.5, 5.13 - */ - -import React, { useState } from 'react'; -import { - TextInput as RNTextInput, - View, - Text, - StyleSheet, - StyleProp, - ViewStyle, - TextStyle, - KeyboardTypeOptions, - PixelRatio, -} from 'react-native'; -import { useDesignSystem } from '../theme/useDesignSystem'; - -export interface TextInputProps { - /** - * Current input value - */ - value: string; - - /** - * Callback when text changes - */ - onChangeText: (text: string) => void; - - /** - * Placeholder text - */ - placeholder?: string; - - /** - * Enable multiline input - * @default false - */ - multiline?: boolean; - - /** - * Number of lines for multiline input - * @default 4 - */ - numberOfLines?: number; - - /** - * Maximum character length - */ - maxLength?: number; - - /** - * Keyboard type - * @default 'default' - */ - keyboardType?: KeyboardTypeOptions; - - /** - * Auto-capitalization behavior - * @default 'sentences' - */ - autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters'; - - /** - * Enable auto-correction - * @default true - */ - autoCorrect?: boolean; - - /** - * Whether input is editable - * @default true - */ - editable?: boolean; - - /** - * Error message to display - * When provided, input shows error state - */ - error?: string; - - /** - * Label text above input - */ - label?: string; - - /** - * Optional icon component to display - */ - icon?: React.ReactNode; - - /** - * Custom style overrides for container - */ - style?: StyleProp; - - /** - * Custom style overrides for input - */ - inputStyle?: StyleProp; - - /** - * Callback when input is focused - */ - onFocus?: () => void; - - /** - * Callback when input loses focus - */ - onBlur?: () => void; - - /** - * Accessibility label for screen readers - * If not provided, uses label text as accessibility label - */ - accessibilityLabel?: string; - - /** - * Accessibility hint for screen readers - * Provides additional context about the input field - */ - accessibilityHint?: string; -} - -/** - * TextInput component with focus states and error handling - */ -export const TextInput: React.FC = ({ - value, - onChangeText, - placeholder, - multiline = false, - numberOfLines = 4, - maxLength, - keyboardType = 'default', - autoCapitalize = 'sentences', - autoCorrect = true, - editable = true, - error, - label, - icon, - style, - inputStyle, - onFocus, - onBlur, - accessibilityLabel, - accessibilityHint, -}) => { - const { theme } = useDesignSystem(); - const [isFocused, setIsFocused] = useState(false); - - /** - * Handle focus event - */ - const handleFocus = () => { - setIsFocused(true); - onFocus?.(); - }; - - /** - * Handle blur event - */ - const handleBlur = () => { - setIsFocused(false); - onBlur?.(); - }; - - /** - * Get background color based on state - * - error: errorContainer background - * - focused: surfaceBright background - * - default: surfaceContainerHighest background - */ - const getBackgroundColor = (): string => { - if (error) { - return theme.colors.errorContainer; - } - if (isFocused) { - return theme.colors.surfaceBright; - } - return theme.colors.surfaceContainerHighest; - }; - - /** - * Get border color based on state - * - error: error border - * - focused: primary border - * - default: transparent - */ - const getBorderColor = (): string => { - if (error) { - return theme.colors.error; - } - if (isFocused) { - return theme.colors.primary; - } - return 'transparent'; - }; - - /** - * Get text color based on state - */ - const getTextColor = (): string => { - if (error) { - return theme.colors.onErrorContainer; - } - return theme.colors.onSurface; - }; - - /** - * Get placeholder color - */ - const getPlaceholderColor = (): string => { - return theme.colors.onSurfaceVariant; - }; - - /** - * Check if character limit is exceeded - */ - const isOverLimit = maxLength ? value.length > maxLength : false; - - /** - * Get character counter color - */ - const getCounterColor = (): string => { - if (isOverLimit) { - return theme.colors.error; - } - return theme.colors.onSurfaceVariant; - }; - - const backgroundColor = getBackgroundColor(); - const borderColor = getBorderColor(); - const textColor = getTextColor(); - const placeholderColor = getPlaceholderColor(); - const counterColor = getCounterColor(); - - return ( - - {/* Label */} - {label && ( - - {label} - - )} - - {/* Input Container */} - - {/* Icon */} - {icon && {icon}} - - {/* Text Input */} - - - - {/* Character Counter */} - {maxLength && ( - - {value.length} / {maxLength} - - )} - - {/* Error Message */} - {error && ( - - {error} - - )} - - ); -}; - -const styles = StyleSheet.create({ - container: { - width: '100%', - }, - label: { - textTransform: 'uppercase', - letterSpacing: 0.5, - }, - inputContainer: { - flexDirection: 'row', - alignItems: 'flex-start', - }, - iconContainer: { - marginRight: 8, - paddingTop: 2, - }, - input: { - flex: 1, - padding: 0, - margin: 0, - }, - counter: { - textAlign: 'right', - }, - error: { - // Error message styling - }, -}); diff --git a/packages/mobile-client/src/design-system/components/Toast.tsx b/packages/mobile-client/src/design-system/components/Toast.tsx deleted file mode 100644 index 97c3239..0000000 --- a/packages/mobile-client/src/design-system/components/Toast.tsx +++ /dev/null @@ -1,283 +0,0 @@ -/** - * Toast Notification System - * - * A toast notification component for displaying transient feedback messages. - * Supports success, error, and info variants with auto-dismiss and queuing. - * - * Requirements: 24.1, 24.2, 24.3, 24.4, 24.5, 24.6, 24.7, 24.8, 24.9, 24.10, 24.11 - */ - -import React, { useEffect, useRef, useState } from 'react'; -import { - View, - Text, - StyleSheet, - Animated, - TouchableOpacity, - Platform, - Dimensions, - AccessibilityInfo, -} from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useDesignSystem } from '../theme/useDesignSystem'; -import { Icon } from './Icon'; - -/** - * Toast variant types - */ -export type ToastVariant = 'success' | 'error' | 'info'; - -/** - * Toast message interface - */ -export interface ToastMessage { - id: string; - message: string; - variant: ToastVariant; - duration?: number; -} - -/** - * Global toast function type - */ -type ShowToastFunction = (message: string, variant: ToastVariant, duration?: number) => void; - -/** - * Extend global interface to include showToast - */ -declare global { - // eslint-disable-next-line no-var - var showToast: ShowToastFunction | undefined; -} - -/** - * Toast component props - */ -interface ToastProps { - message: ToastMessage; - onDismiss: (id: string) => void; -} - -/** - * Single Toast component with slide-in animation - */ -const Toast: React.FC = ({ message, onDismiss }) => { - const { theme } = useDesignSystem(); - const insets = useSafeAreaInsets(); - const translateY = useRef(new Animated.Value(100)).current; - const opacity = useRef(new Animated.Value(0)).current; - - useEffect(() => { - // Announce toast message to screen readers - // Requirement 14.11: Announce toast notifications - const variantLabel = - message.variant === 'success' - ? 'Success' - : message.variant === 'error' - ? 'Error' - : 'Information'; - AccessibilityInfo.announceForAccessibility(`${variantLabel}: ${message.message}`); - - // Slide in animation - Animated.parallel([ - Animated.timing(translateY, { - toValue: 0, - duration: 300, - useNativeDriver: true, - }), - Animated.timing(opacity, { - toValue: 1, - duration: 300, - useNativeDriver: true, - }), - ]).start(); - - // Auto-dismiss after duration (default 3000ms) - // Requirement 24.5: Auto-dismiss after 3000ms - const duration = message.duration || 3000; - const timer = setTimeout(() => { - handleDismiss(); - }, duration); - - return () => clearTimeout(timer); - }, []); - - /** - * Handle dismiss with slide-out animation - * Requirement 24.6: Manual dismissal support - */ - const handleDismiss = () => { - Animated.parallel([ - Animated.timing(translateY, { - toValue: 100, - duration: 200, - useNativeDriver: true, - }), - Animated.timing(opacity, { - toValue: 0, - duration: 200, - useNativeDriver: true, - }), - ]).start(() => { - onDismiss(message.id); - }); - }; - - /** - * Get toast colors based on variant - * Requirements: 24.7, 24.8, 24.9 - */ - const getToastColors = () => { - switch (message.variant) { - case 'success': - return { - backgroundColor: `${theme.colors.secondary}1A`, // 10% opacity - borderColor: theme.colors.secondary, - iconColor: theme.colors.secondary, - icon: 'check-circle' as const, - }; - case 'error': - return { - backgroundColor: `${theme.colors.error}1A`, // 10% opacity - borderColor: theme.colors.error, - iconColor: theme.colors.error, - icon: 'error' as const, - }; - case 'info': - return { - backgroundColor: `${theme.colors.primary}1A`, // 10% opacity - borderColor: theme.colors.primary, - iconColor: theme.colors.primary, - icon: 'info' as const, - }; - } - }; - - const colors = getToastColors(); - - return ( - - - - {message.message} - - - - - - ); -}; - -/** - * Toast Container component manages toast queue - * Requirement 24.11: Queue multiple toasts and display sequentially - */ -export const ToastContainer: React.FC = () => { - const [toasts, setToasts] = useState([]); - - /** - * Add toast to queue - */ - const addToast = (message: string, variant: ToastVariant, duration?: number) => { - const id = Date.now().toString(); - setToasts((prev) => [...prev, { id, message, variant, duration }]); - }; - - /** - * Remove toast from queue - */ - const removeToast = (id: string) => { - setToasts((prev) => prev.filter((toast) => toast.id !== id)); - }; - - // Expose addToast globally for easy access - useEffect(() => { - global.showToast = addToast; - return () => { - delete global.showToast; - }; - }, []); - - return ( - - {toasts.map((toast) => ( - - ))} - - ); -}; - -/** - * Helper function to show toast notifications - * Usage: showToast('Message', 'success') - */ -export const showToast = (message: string, variant: ToastVariant = 'info', duration?: number) => { - if (global.showToast) { - global.showToast(message, variant, duration); - } -}; - -const styles = StyleSheet.create({ - container: { - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - alignItems: 'center', - zIndex: 9999, - }, - toast: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - paddingHorizontal: 16, - paddingVertical: 12, - marginHorizontal: 16, - borderRadius: 12, - borderWidth: 1, - maxWidth: Dimensions.get('window').width - 32, - ...Platform.select({ - ios: { - shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.3, - shadowRadius: 8, - }, - android: { - elevation: 8, - }, - }), - }, - message: { - flex: 1, - lineHeight: 20, - }, -}); diff --git a/packages/mobile-client/src/design-system/components/Toggle.tsx b/packages/mobile-client/src/design-system/components/Toggle.tsx deleted file mode 100644 index c988019..0000000 --- a/packages/mobile-client/src/design-system/components/Toggle.tsx +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Toggle Component - * - * A toggle switch component with label, description, and haptic feedback. - * Supports enabled/disabled states with design system colors. - * - * Requirements: 10.5, 7.4, 12.10 - */ - -import React from 'react'; -import { View, Switch, Text, StyleSheet, StyleProp, ViewStyle } from 'react-native'; -import { useDesignSystem } from '../theme/useDesignSystem'; -import { triggerHapticFeedback } from '../../utils/platformAdaptations'; - -export interface ToggleProps { - /** - * Current toggle value - */ - value: boolean; - - /** - * Callback when value changes - */ - onValueChange: (value: boolean) => void; - - /** - * Disabled state - * @default false - */ - disabled?: boolean; - - /** - * Label text displayed above the toggle - */ - label?: string; - - /** - * Description text displayed below the label - */ - description?: string; - - /** - * Enable haptic feedback on value change - * @default true - */ - hapticFeedback?: boolean; - - /** - * Custom style overrides - */ - style?: StyleProp; - - /** - * Accessibility label for screen readers - * If not provided, uses label text as accessibility label - */ - accessibilityLabel?: string; - - /** - * Accessibility hint for screen readers - * Provides additional context about what the toggle controls - */ - accessibilityHint?: string; -} - -/** - * Toggle switch component with label and description - */ -export const Toggle: React.FC = ({ - value, - onValueChange, - disabled = false, - label, - description, - hapticFeedback = true, - style, - accessibilityLabel, - accessibilityHint, -}) => { - const { theme } = useDesignSystem(); - - /** - * Handle value change with haptic feedback - * Requirements: 22.5, 22.6, 22.7 - */ - const handleValueChange = async (newValue: boolean) => { - if (disabled) return; - - // Trigger platform-specific haptic feedback - if (hapticFeedback) { - await triggerHapticFeedback('selection'); - } - - onValueChange(newValue); - }; - - return ( - - {/* Label and description section */} - {(label || description) && ( - - {label && ( - - {label} - - )} - {description && ( - - {description} - - )} - - )} - - {/* Toggle switch */} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - minHeight: 44, // Minimum touch target size - }, - textContainer: { - flex: 1, - marginRight: 16, - }, - label: { - marginBottom: 4, - }, - description: { - lineHeight: 18, - }, - switch: { - // Platform-specific adjustments handled by React Native - }, -}); diff --git a/packages/mobile-client/src/design-system/components/index.ts b/packages/mobile-client/src/design-system/components/index.ts deleted file mode 100644 index b24343e..0000000 --- a/packages/mobile-client/src/design-system/components/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Design System Components - * - * This module exports all reusable UI components - * for the Obsidian UI design system. - */ - -export * from './Button'; -export { Card } from './Card'; -export type { CardProps, CardVariant } from './Card'; -export { Chip } from './Chip'; -export type { ChipProps, ChipVariant, ChipSize } from './Chip'; -export { TextInput } from './TextInput'; -export type { TextInputProps } from './TextInput'; -export { Toggle } from './Toggle'; -export type { ToggleProps } from './Toggle'; -export { StatusIndicator } from './StatusIndicator'; -export type { - StatusIndicatorProps, - ConnectionStatus, - StatusIndicatorSize, -} from './StatusIndicator'; -export { Icon, IconNames } from './Icon'; -export type { IconProps, IconName, IconWeight } from './Icon'; -export { ProgressBar } from './ProgressBar'; -export type { ProgressBarProps, ProgressBarVariant } from './ProgressBar'; -export { Skeleton } from './Skeleton'; -export type { SkeletonProps } from './Skeleton'; -export { ToastContainer, showToast } from './Toast'; -export type { ToastMessage, ToastVariant } from './Toast'; -export { GlassContainer } from './GlassContainer'; -export type { GlassContainerProps } from './GlassContainer'; diff --git a/packages/mobile-client/src/design-system/index.ts b/packages/mobile-client/src/design-system/index.ts deleted file mode 100644 index 4fb1662..0000000 --- a/packages/mobile-client/src/design-system/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Design System - * - * Main entry point for the Obsidian UI design system. - * Exports all tokens, theme utilities, typography, and components. - */ - -// Export design tokens -export * from './tokens'; - -// Export theme system -export * from './theme'; - -// Export typography -export * from './typography'; - -// Export components -export * from './components'; diff --git a/packages/mobile-client/src/design-system/theme/ThemeContext.tsx b/packages/mobile-client/src/design-system/theme/ThemeContext.tsx deleted file mode 100644 index ab5cb26..0000000 --- a/packages/mobile-client/src/design-system/theme/ThemeContext.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Theme Context - * - * React Context for theme configuration and management. - * Provides theme access throughout the component tree. - * - * Requirements: 1.6 - */ - -import { createContext } from 'react'; -import type { ThemeConfig, ThemeConfiguration } from './types'; -import { defaultThemeConfiguration } from './types'; -import { createTheme } from './createTheme'; - -/** - * ThemeContextValue interface defines the shape of the theme context. - * Provides access to the current theme and configuration update function. - */ -export interface ThemeContextValue { - /** - * Current theme object with all design tokens - */ - theme: ThemeConfig; - - /** - * Current theme configuration (user preferences) - */ - config: ThemeConfiguration; - - /** - * Function to update theme configuration - */ - setConfig: (config: ThemeConfiguration) => void; -} - -/** - * Default theme context value. - * Used as fallback when ThemeProvider is not in the component tree. - */ -const defaultTheme = createTheme(defaultThemeConfiguration); - -const defaultContextValue: ThemeContextValue = { - theme: defaultTheme, - config: defaultThemeConfiguration, - setConfig: () => { - console.warn('setConfig called outside of ThemeProvider'); - }, -}; - -/** - * ThemeContext provides theme configuration throughout the app. - * Must be used within a ThemeProvider component. - */ -export const ThemeContext = createContext(defaultContextValue); diff --git a/packages/mobile-client/src/design-system/theme/ThemeProvider.tsx b/packages/mobile-client/src/design-system/theme/ThemeProvider.tsx deleted file mode 100644 index b1f7188..0000000 --- a/packages/mobile-client/src/design-system/theme/ThemeProvider.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Theme Provider Component - * - * Provides theme configuration to the entire app via React Context. - * Handles theme persistence with AsyncStorage and theme updates. - * - * Requirements: 1.6, 20.1, 20.5, 20.6, 20.7 - */ - -import React, { useState, useEffect, useMemo, type ReactNode } from 'react'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { ThemeContext } from './ThemeContext'; -import { createTheme } from './createTheme'; -import { defaultThemeConfiguration, type ThemeConfiguration } from './types'; - -/** - * AsyncStorage key for theme configuration persistence - */ -const THEME_STORAGE_KEY = '@codelink/theme_config'; - -/** - * ThemeProvider props - */ -interface ThemeProviderProps { - children: ReactNode; -} - -/** - * Loads theme configuration from AsyncStorage. - * Returns default configuration if storage is unavailable or empty. - */ -async function loadThemeConfiguration(): Promise { - try { - const stored = await AsyncStorage.getItem(THEME_STORAGE_KEY); - if (stored) { - const parsed = JSON.parse(stored) as ThemeConfiguration; - return parsed; - } - } catch (error) { - console.error('Failed to load theme configuration from AsyncStorage:', error); - } - return defaultThemeConfiguration; -} - -/** - * Saves theme configuration to AsyncStorage. - */ -async function saveThemeConfiguration(config: ThemeConfiguration): Promise { - try { - await AsyncStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(config)); - } catch (error) { - console.error('Failed to save theme configuration to AsyncStorage:', error); - } -} - -/** - * ThemeProvider component wraps the app and provides theme configuration - * via React Context. Handles loading and persisting theme preferences. - */ -export const ThemeProvider: React.FC = ({ children }) => { - const [config, setConfigState] = useState(defaultThemeConfiguration); - const [isLoading, setIsLoading] = useState(true); - - // Load saved theme configuration on mount - useEffect(() => { - loadThemeConfiguration() - .then((loadedConfig) => { - setConfigState(loadedConfig); - }) - .finally(() => { - setIsLoading(false); - }); - }, []); - - // Create theme object from configuration - // Memoized to avoid unnecessary recalculations - const theme = useMemo(() => createTheme(config), [config]); - - // Update configuration and persist to AsyncStorage - const setConfig = (newConfig: ThemeConfiguration) => { - setConfigState(newConfig); - // Save asynchronously without blocking UI - saveThemeConfiguration(newConfig).catch((error) => { - console.error('Failed to persist theme configuration:', error); - }); - }; - - // Context value with theme, config, and setter - const contextValue = useMemo( - () => ({ - theme, - config, - setConfig, - }), - [theme, config] - ); - - // Show nothing while loading theme preferences - // This prevents flash of default theme before loading saved preferences - if (isLoading) { - return null; - } - - return {children}; -}; diff --git a/packages/mobile-client/src/design-system/theme/createTheme.ts b/packages/mobile-client/src/design-system/theme/createTheme.ts deleted file mode 100644 index 152af08..0000000 --- a/packages/mobile-client/src/design-system/theme/createTheme.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Create Theme Function - * - * Creates a complete theme object from user configuration. - * Applies custom colors and high contrast adjustments as needed. - * - * Requirements: 1.6, 1.7, 14.6, 14.10 - */ - -import { colorTokens, type ColorTokens } from '../tokens/colors'; -import { typographyTokens } from '../tokens/typography'; -import { spacingTokens } from '../tokens/spacing'; -import { borderRadiusTokens } from '../tokens/borderRadius'; -import type { ThemeConfig, ThemeConfiguration } from './types'; - -/** - * High contrast color adjustments. - * Increases contrast ratios to 7:1 minimum for WCAG AAA compliance. - * - * WCAG AAA requires: - * - 7:1 contrast ratio for normal text (< 18pt or < 14pt bold) - * - 4.5:1 contrast ratio for large text (>= 18pt or >= 14pt bold) - * - * Base surface colors for contrast calculations: - * - surface: #131313 (very dark gray) - * - surfaceContainerLowest: #0f0f0f (darkest) - * - surfaceContainerLow: #1a1a1a - * - surfaceContainerHigh: #2a2a2a - * - surfaceContainerHighest: #353535 - */ -const highContrastColorAdjustments: Partial = { - // Text colors - increased contrast for 7:1 ratio - onSurface: '#ffffff', // Pure white for maximum contrast (21:1 on #131313) - onSurfaceVariant: '#e5e5e5', // Very light gray for secondary text (10.5:1 on #131313) - onBackground: '#ffffff', // Pure white for maximum contrast - - // Primary colors - enhanced brightness for better visibility - primary: '#a8d5ff', // Brighter light blue (12:1 on #131313) - primaryContainer: '#6eb3ff', // Brighter blue for keywords (7.5:1 on #0f0f0f) - - // Secondary colors - enhanced brightness - secondary: '#7de8d1', // Brighter teal/green (11:1 on #131313) - secondaryContainer: '#5fd4bd', // Brighter secondary container (8:1 on #1a1a1a) - - // Tertiary colors - enhanced brightness - tertiary: '#ffc9b3', // Brighter peach/orange (10:1 on #131313) - tertiaryContainer: '#ffb199', // Brighter tertiary container (8.5:1 on #1a1a1a) - - // Error colors - enhanced brightness - error: '#ffc7c2', // Brighter light red (10.5:1 on #131313) - errorContainer: '#ffb3ad', // Brighter error container (9:1 on #1a1a1a) - - // Outline colors - increased visibility for borders - outline: '#999999', // Much brighter outline (5.5:1 on #131313) - outlineVariant: '#737373', // Brighter outline variant (3.5:1 on #131313) - - // Surface colors - increased separation between layers - surfaceBright: '#505050', // Brighter focused states (3.5:1 on #131313) - surfaceContainerHighest: '#484848', // Brighter highest container (3:1 on #131313) - surfaceContainerHigh: '#3d3d3d', // Brighter high container - surfaceContainer: '#2d2d2d', // Brighter default container - - // On-container colors - ensure text on containers meets 7:1 - onPrimaryContainer: '#ffffff', // White on primary container - onSecondaryContainer: '#ffffff', // White on secondary container - onTertiaryContainer: '#000000', // Black on tertiary container (better contrast) - onErrorContainer: '#000000', // Black on error container (better contrast) -}; - -/** - * Creates a complete theme object from user configuration. - * - * @param config - User theme configuration - * @returns Complete theme object with all design tokens - */ -export function createTheme(config: ThemeConfiguration): ThemeConfig { - // Start with default color tokens - let colors: ColorTokens = { ...colorTokens }; - - // Apply high contrast adjustments if enabled - if (config.highContrast) { - colors = { - ...colors, - ...highContrastColorAdjustments, - }; - } - - // Apply custom color overrides if provided - if (config.customColors) { - colors = { - ...colors, - ...config.customColors, - }; - } - - // Note: Light mode is not yet implemented - // When implemented, this function should switch color palettes based on config.mode - if (config.mode === 'light') { - console.warn('Light mode is not yet implemented. Using dark mode.'); - } - - // Return complete theme configuration - return { - colors, - typography: typographyTokens, - spacing: spacingTokens, - borderRadius: borderRadiusTokens, - }; -} diff --git a/packages/mobile-client/src/design-system/theme/index.ts b/packages/mobile-client/src/design-system/theme/index.ts deleted file mode 100644 index 08fd7e4..0000000 --- a/packages/mobile-client/src/design-system/theme/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Design System Theme - * - * This module exports theme configuration, theme provider, - * and theme-related utilities for the Obsidian UI design system. - */ - -export * from './types'; -export * from './createTheme'; -export * from './ThemeContext'; -export * from './ThemeProvider'; -export * from './useDesignSystem'; diff --git a/packages/mobile-client/src/design-system/theme/types.ts b/packages/mobile-client/src/design-system/theme/types.ts deleted file mode 100644 index ea41aa6..0000000 --- a/packages/mobile-client/src/design-system/theme/types.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Theme Configuration Types - * - * Defines TypeScript interfaces for theme configuration including: - * - ThemeConfig interface with all design tokens - * - ThemeConfiguration interface for user preferences - * - Default theme configuration - * - * Requirements: 1.6, 1.7 - */ - -import type { ColorTokens } from '../tokens/colors'; -import type { TypographyTokens } from '../tokens/typography'; -import type { SpacingTokens } from '../tokens/spacing'; -import type { BorderRadiusTokens } from '../tokens/borderRadius'; - -/** - * ThemeConfig interface defines the complete theme configuration - * including all design tokens (colors, typography, spacing, border radius). - */ -export interface ThemeConfig { - colors: ColorTokens; - typography: TypographyTokens; - spacing: SpacingTokens; - borderRadius: BorderRadiusTokens; -} - -/** - * ThemeConfiguration interface defines user-configurable theme preferences. - * These preferences control the theme mode and accessibility features. - */ -export interface ThemeConfiguration { - /** - * Theme mode: 'dark', 'light', or 'auto' (follows system preference) - * Currently only dark mode is fully implemented. - */ - mode: 'dark' | 'light' | 'auto'; - - /** - * High contrast mode for improved accessibility. - * When enabled, increases contrast ratios to 7:1 minimum. - */ - highContrast: boolean; - - /** - * Optional custom color overrides. - * Allows partial customization of the color palette. - */ - customColors?: Partial; -} - -/** - * Default theme configuration. - * Uses dark mode as the primary theme with standard contrast. - */ -export const defaultThemeConfiguration: ThemeConfiguration = { - mode: 'dark', - highContrast: false, - customColors: undefined, -}; diff --git a/packages/mobile-client/src/design-system/theme/useDesignSystem.ts b/packages/mobile-client/src/design-system/theme/useDesignSystem.ts deleted file mode 100644 index 1b8a72e..0000000 --- a/packages/mobile-client/src/design-system/theme/useDesignSystem.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * useDesignSystem Hook - * - * Custom React hook for accessing the design system theme. - * Provides type-safe access to theme configuration and update function. - * - * Requirements: 1.6 - */ - -import { useContext } from 'react'; -import { ThemeContext } from './ThemeContext'; - -/** - * Hook for accessing the design system theme. - * Must be used within a ThemeProvider component. - * - * @returns Theme context value with theme, config, and setConfig - * @throws Error if used outside of ThemeProvider - * - * @example - * ```tsx - * function MyComponent() { - * const { theme, config, setConfig } = useDesignSystem(); - * - * return ( - * - * - * Hello World - * - * - * ); - * } - * ``` - */ -export function useDesignSystem() { - const context = useContext(ThemeContext); - - if (!context) { - throw new Error('useDesignSystem must be used within a ThemeProvider'); - } - - return context; -} diff --git a/packages/mobile-client/src/design-system/tokens/borderRadius.ts b/packages/mobile-client/src/design-system/tokens/borderRadius.ts deleted file mode 100644 index 858ac15..0000000 --- a/packages/mobile-client/src/design-system/tokens/borderRadius.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Border Radius Tokens - * - * Defines all border radius tokens for the Obsidian UI design system. - * Border radius values range from sm (2px) for subtle rounding to full (9999px) - * for pill-shaped elements like chips and status indicators. - * - * Requirements: 1.5 - */ - -/** - * BorderRadiusTokens interface defines all border radius values used in the design system. - * Values are in pixels and provide consistent corner rounding across components. - */ -export interface BorderRadiusTokens { - sm: number; // 0.125rem / 2px - subtle rounding - md: number; // 0.375rem / 6px - medium rounding - lg: number; // 0.5rem / 8px - large rounding (default for cards) - xl: number; // 0.75rem / 12px - extra large rounding - '2xl': number; // 1rem / 16px - 2x extra large rounding - full: number; // 9999px - fully rounded (pills, circles) -} - -/** - * Default border radius scale matching the design specifications. - * All values are in pixels for React Native compatibility. - */ -export const defaultBorderRadiusTokens: BorderRadiusTokens = { - sm: 2, // 0.125rem - subtle rounding - md: 6, // 0.375rem - medium rounding - lg: 8, // 0.5rem - large rounding (default for cards) - xl: 12, // 0.75rem - extra large rounding - '2xl': 16, // 1rem - 2x extra large rounding - full: 9999, // fully rounded (pills, circles) -}; - -/** - * Export the default border radius tokens as the main export. - * This can be overridden by theme configuration in the future. - */ -export const borderRadiusTokens = defaultBorderRadiusTokens; diff --git a/packages/mobile-client/src/design-system/tokens/colors.ts b/packages/mobile-client/src/design-system/tokens/colors.ts deleted file mode 100644 index d7250e4..0000000 --- a/packages/mobile-client/src/design-system/tokens/colors.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Color Tokens - * - * Defines all color tokens for the Obsidian UI design system including: - * - Surface hierarchy colors for tonal layering - * - Primary, secondary, tertiary, and error accent colors - * - Semantic color tokens for text and outlines - * - */ - -/** - * ColorTokens interface defines all color values used in the design system. - * Colors follow Material Design 3 naming conventions with surface hierarchy - * for tonal layering instead of drop shadows or borders. - */ -export interface ColorTokens { - // Surface hierarchy - used for tonal layering to create depth - surface: string; - surfaceContainerLowest: string; - surfaceContainerLow: string; - surfaceContainer: string; - surfaceContainerHigh: string; - surfaceContainerHighest: string; - surfaceVariant: string; - surfaceBright: string; - surfaceDim: string; - - // Primary colors - main brand color (#95ccff - light blue) - primary: string; - primaryContainer: string; - onPrimary: string; - onPrimaryContainer: string; - - // Secondary colors - accent color (#61dac1 - teal/green) - secondary: string; - secondaryContainer: string; - onSecondary: string; - onSecondaryContainer: string; - - // Tertiary colors - accent color (#fab79d - peach/orange) - tertiary: string; - tertiaryContainer: string; - onTertiary: string; - onTertiaryContainer: string; - - // Error colors - error states (#ffb4ab - light red) - error: string; - errorContainer: string; - onError: string; - onErrorContainer: string; - - // Text colors - for content on various surfaces - onSurface: string; - onSurfaceVariant: string; - onBackground: string; - - // Outline colors - for borders and dividers - outline: string; - outlineVariant: string; -} - -/** - * Default dark theme color palette matching the Obsidian IDE aesthetic. - * All colors are defined to match the Tailwind configuration in Stitch designs. - */ -export const defaultColorPalette: ColorTokens = { - // Surface hierarchy - dark theme with subtle variations for depth - surface: '#131313', // Base surface (darkest) - surfaceContainerLowest: '#0f0f0f', // Lowest container (code blocks) - surfaceContainerLow: '#1a1a1a', // Low container (cards) - surfaceContainer: '#1f1f1f', // Default container - surfaceContainerHigh: '#2a2a2a', // High container (elevated elements) - surfaceContainerHighest: '#353535', // Highest container (inputs, inactive states) - surfaceVariant: '#2a2a2a', // Variant surface - surfaceBright: '#3a3a3a', // Bright surface (focused states) - surfaceDim: '#0a0a0a', // Dim surface - - // Primary colors - light blue (#95ccff) - primary: '#95ccff', // Primary brand color - primaryContainer: '#569cd6', // Primary container (darker blue for keywords) - onPrimary: '#000000', // Text on primary - onPrimaryContainer: '#ffffff', // Text on primary container - - // Secondary colors - teal/green (#61dac1) - secondary: '#61dac1', // Secondary accent (success, active states) - secondaryContainer: '#4db8a3', // Secondary container - onSecondary: '#000000', // Text on secondary - onSecondaryContainer: '#ffffff', // Text on secondary container - - // Tertiary colors - peach/orange (#fab79d) - tertiary: '#fab79d', // Tertiary accent (warnings, highlights) - tertiaryContainer: '#e89b7f', // Tertiary container - onTertiary: '#000000', // Text on tertiary - onTertiaryContainer: '#ffffff', // Text on tertiary container - - // Error colors - light red (#ffb4ab) - error: '#ffb4ab', // Error state - errorContainer: '#ff8a80', // Error container - onError: '#000000', // Text on error - onErrorContainer: '#ffffff', // Text on error container - - // Text colors - optimized for dark theme readability - onSurface: '#ffffff', // Primary text on surface - onSurfaceVariant: '#b0b0b0', // Secondary text, dimmed - onBackground: '#ffffff', // Text on background - - // Outline colors - for borders and dividers - outline: '#5a5a5a', // Standard outline - outlineVariant: '#3a3a3a', // Subtle outline variant -}; - -/** - * Export the default palette as the main color tokens export. - * This can be overridden by theme configuration in the future. - */ -export const colorTokens = defaultColorPalette; diff --git a/packages/mobile-client/src/design-system/tokens/index.ts b/packages/mobile-client/src/design-system/tokens/index.ts deleted file mode 100644 index 7afab40..0000000 --- a/packages/mobile-client/src/design-system/tokens/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Design System Tokens - * - * This module exports all design tokens including colors, typography, - * spacing, and border radius tokens for the Obsidian UI design system. - */ - -export * from './colors'; -export * from './typography'; -export * from './spacing'; -export * from './borderRadius'; diff --git a/packages/mobile-client/src/design-system/tokens/spacing.ts b/packages/mobile-client/src/design-system/tokens/spacing.ts deleted file mode 100644 index e18c357..0000000 --- a/packages/mobile-client/src/design-system/tokens/spacing.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Spacing Tokens - * - * Defines all spacing tokens for the Obsidian UI design system. - * Spacing scale follows a consistent progression from xs (4px) to 4xl (64px) - * for margins, padding, and gaps throughout the interface. - * - * Requirements: 1.4 - */ - -/** - * SpacingTokens interface defines all spacing values used in the design system. - * Values are in pixels and follow a consistent scale for predictable layouts. - */ -export interface SpacingTokens { - xs: number; // 0.25rem / 4px - minimal spacing - sm: number; // 0.5rem / 8px - small spacing - md: number; // 0.75rem / 12px - medium spacing - lg: number; // 1rem / 16px - large spacing (base unit) - xl: number; // 1.5rem / 24px - extra large spacing - '2xl': number; // 2rem / 32px - 2x extra large spacing - '3xl': number; // 3rem / 48px - 3x extra large spacing - '4xl': number; // 4rem / 64px - 4x extra large spacing -} - -/** - * Default spacing scale matching the design specifications. - * All values are in pixels for React Native compatibility. - */ -export const defaultSpacingTokens: SpacingTokens = { - xs: 4, // 0.25rem - sm: 8, // 0.5rem - md: 12, // 0.75rem - lg: 16, // 1rem (base unit) - xl: 24, // 1.5rem - '2xl': 32, // 2rem - '3xl': 48, // 3rem - '4xl': 64, // 4rem -}; - -/** - * Export the default spacing tokens as the main export. - * This can be overridden by theme configuration in the future. - */ -export const spacingTokens = defaultSpacingTokens; diff --git a/packages/mobile-client/src/design-system/tokens/typography.ts b/packages/mobile-client/src/design-system/tokens/typography.ts deleted file mode 100644 index e121467..0000000 --- a/packages/mobile-client/src/design-system/tokens/typography.ts +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Typography Tokens - * - * Defines all typography tokens for the Obsidian UI design system including: - * - Font families (Manrope, Inter, Space Grotesk, monospace) - * - Typography size scale (displayLg through labelSm) - * - Font weights (regular through extrabold) - * - Line heights (tight, normal, relaxed) - * - * Font sizes are scalable and respect system text size preferences using - * PixelRatio.getFontScale() for accessibility compliance. - * - * Requirements: 1.3, 14.9 - */ - -import { PixelRatio } from 'react-native'; - -/** - * TypographyTokens interface defines all typography values used in the design system. - * Typography follows the Obsidian IDE aesthetic with editorial font choices: - * - Manrope for headlines (sophisticated, geometric sans-serif) - * - Inter for body text (highly legible, optimized for screens) - * - Space Grotesk for labels (technical, monospace-inspired) - * - Monospace for code (Fira Code or system fallback) - */ -export interface TypographyTokens { - fonts: { - headline: string; // Manrope - for headlines and display text - body: string; // Inter - for body text and paragraphs - label: string; // Space Grotesk - for labels and metadata - mono: string; // Fira Code or system monospace - for code - }; - sizes: { - displayLg: number; // 3.5rem / 56px - largest display text - displayMd: number; // 2.8rem / 44.8px - medium display text - displaySm: number; // 2.25rem / 36px - small display text - headlineLg: number; // 2rem / 32px - large headlines - headlineMd: number; // 1.75rem / 28px - medium headlines - headlineSm: number; // 1.5rem / 24px - small headlines - titleLg: number; // 1.375rem / 22px - large titles - titleMd: number; // 1.125rem / 18px - medium titles - titleSm: number; // 0.875rem / 14px - small titles - bodyLg: number; // 1rem / 16px - large body text - bodyMd: number; // 0.875rem / 14px - medium body text - bodySm: number; // 0.75rem / 12px - small body text - labelLg: number; // 0.875rem / 14px - large labels - labelMd: number; // 0.75rem / 12px - medium labels - labelSm: number; // 0.6875rem / 11px - small labels - }; - weights: { - regular: number; // 400 - normal text weight - medium: number; // 500 - slightly emphasized - semibold: number; // 600 - emphasized text - bold: number; // 700 - strong emphasis - extrabold: number; // 800 - maximum emphasis - }; - lineHeights: { - tight: number; // 1.2 - compact line spacing - normal: number; // 1.5 - standard line spacing - relaxed: number; // 1.75 - loose line spacing - }; -} - -/** - * Base font sizes (before scaling) matching the Obsidian IDE aesthetic. - * These values will be multiplied by the system font scale for accessibility. - */ -const baseFontSizes = { - displayLg: 56, // 3.5rem - displayMd: 44.8, // 2.8rem - displaySm: 36, // 2.25rem - headlineLg: 32, // 2rem - headlineMd: 28, // 1.75rem - headlineSm: 24, // 1.5rem - titleLg: 22, // 1.375rem - titleMd: 18, // 1.125rem - titleSm: 14, // 0.875rem - bodyLg: 16, // 1rem - bodyMd: 14, // 0.875rem - bodySm: 12, // 0.75rem - labelLg: 14, // 0.875rem - labelMd: 12, // 0.75rem - labelSm: 11, // 0.6875rem -}; - -/** - * Gets the system font scale multiplier for accessibility. - * This respects user preferences for larger text sizes. - * - * @returns Font scale multiplier (typically 1.0 for default, higher for large text) - */ -export function getFontScale(): number { - return PixelRatio.getFontScale(); -} - -/** - * Scales a font size based on system text size preferences. - * - * @param baseSize - The base font size in pixels - * @returns Scaled font size respecting system preferences - */ -export function scaleFont(baseSize: number): number { - return baseSize * getFontScale(); -} - -/** - * Default typography configuration matching the Obsidian IDE aesthetic. - * Font families will be loaded via Expo Font with appropriate fallbacks. - * Font sizes are dynamically scaled based on system text size preferences. - */ -export const defaultTypographyTokens: TypographyTokens = { - fonts: { - headline: 'Manrope', // Geometric sans-serif for headlines - body: 'Inter', // Optimized for body text readability - label: 'SpaceGrotesk', // Technical aesthetic for labels - mono: 'FiraCode', // Monospace for code (fallback to system) - }, - sizes: { - displayLg: scaleFont(baseFontSizes.displayLg), - displayMd: scaleFont(baseFontSizes.displayMd), - displaySm: scaleFont(baseFontSizes.displaySm), - headlineLg: scaleFont(baseFontSizes.headlineLg), - headlineMd: scaleFont(baseFontSizes.headlineMd), - headlineSm: scaleFont(baseFontSizes.headlineSm), - titleLg: scaleFont(baseFontSizes.titleLg), - titleMd: scaleFont(baseFontSizes.titleMd), - titleSm: scaleFont(baseFontSizes.titleSm), - bodyLg: scaleFont(baseFontSizes.bodyLg), - bodyMd: scaleFont(baseFontSizes.bodyMd), - bodySm: scaleFont(baseFontSizes.bodySm), - labelLg: scaleFont(baseFontSizes.labelLg), - labelMd: scaleFont(baseFontSizes.labelMd), - labelSm: scaleFont(baseFontSizes.labelSm), - }, - weights: { - regular: 400, // Normal weight - medium: 500, // Medium weight - semibold: 600, // Semibold weight - bold: 700, // Bold weight - extrabold: 800, // Extrabold weight - }, - lineHeights: { - tight: 1.2, // Compact spacing for headlines - normal: 1.5, // Standard spacing for body text - relaxed: 1.75, // Loose spacing for readability - }, -}; - -/** - * Export the default typography tokens as the main export. - * This can be overridden by theme configuration in the future. - */ -export const typographyTokens = defaultTypographyTokens; diff --git a/packages/mobile-client/src/design-system/typography/DynamicTextExample.tsx b/packages/mobile-client/src/design-system/typography/DynamicTextExample.tsx deleted file mode 100644 index fbf90a9..0000000 --- a/packages/mobile-client/src/design-system/typography/DynamicTextExample.tsx +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Dynamic Text Sizing Example - * - * Demonstrates how the Text component respects system text size preferences. - * This example can be used for manual testing with different text size settings. - * - * To test: - * 1. iOS: Settings > Display & Brightness > Text Size - * 2. Android: Settings > Display > Font size - * - * Requirements: 14.9 - */ - -import React from 'react'; -import { View, ScrollView, StyleSheet, PixelRatio } from 'react-native'; -import { Text } from './Text'; - -/** - * Example component showing all text variants with dynamic sizing. - * Font sizes will automatically scale based on system preferences. - */ -export const DynamicTextExample: React.FC = () => { - const fontScale = PixelRatio.getFontScale(); - - return ( - - - - Dynamic Text Sizing Demo - - - Current font scale: {fontScale.toFixed(2)}x - - - Change your system text size settings to see this text scale automatically. - - - - - - Display Variants - - - Display Large - - - Display Medium - - - Display Small - - - - - - Headline Variants - - - Headline Large - - - Headline Medium - - - Headline Small - - - - - - Title Variants - - - Title Large - - - Title Medium - - - Title Small - - - - - - Body Variants - - - Body Large - This is a longer text to demonstrate how body text scales with system - preferences. The layout should adapt gracefully to larger text sizes. - - - Body Medium - This is a longer text to demonstrate how body text scales with system - preferences. The layout should adapt gracefully to larger text sizes. - - - Body Small - This is a longer text to demonstrate how body text scales with system - preferences. The layout should adapt gracefully to larger text sizes. - - - - - - Label Variants - - Label Large - Label Medium - Label Small - - - - - Layout Adaptation Test - - - - Card Title - - - This card demonstrates how layouts adapt to larger text. The card height should grow - automatically as text size increases, preventing overflow and maintaining readability. - - - - - Action - - - - - Cancel - - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#131313', - }, - section: { - padding: 16, - gap: 12, - }, - info: { - marginTop: 4, - }, - card: { - backgroundColor: '#1a1a1a', - borderRadius: 12, - padding: 16, - gap: 12, - }, - cardText: { - lineHeight: 20, - }, - buttonRow: { - flexDirection: 'row', - gap: 12, - marginTop: 8, - }, - button: { - paddingVertical: 8, - paddingHorizontal: 16, - borderRadius: 8, - backgroundColor: '#2a2a2a', - minHeight: 44, // Maintains touch target size - justifyContent: 'center', - alignItems: 'center', - }, -}); diff --git a/packages/mobile-client/src/design-system/typography/Text.tsx b/packages/mobile-client/src/design-system/typography/Text.tsx deleted file mode 100644 index 50080e3..0000000 --- a/packages/mobile-client/src/design-system/typography/Text.tsx +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Text Component - * - * Typography component with design system variants and styling. - * Supports all typography scales from display-lg to label-sm with - * automatic font family mapping based on variant type. - * - * Supports dynamic text sizing (Requirement 14.9) by respecting system - * font scale settings (iOS Dynamic Type and Android Font Size). - * - * Requirements: 2.7, 2.8, 2.9, 14.9 - */ - -import React from 'react'; -import { Text as RNText, type TextStyle, type StyleProp, PixelRatio } from 'react-native'; -import { useDesignSystem } from '../theme/useDesignSystem'; -import type { ColorTokens } from '../tokens/colors'; -import type { TypographyTokens } from '../tokens/typography'; - -/** - * Typography variant types - */ -export type TypographyVariant = - | 'display-lg' - | 'display-md' - | 'display-sm' - | 'headline-lg' - | 'headline-md' - | 'headline-sm' - | 'title-lg' - | 'title-md' - | 'title-sm' - | 'body-lg' - | 'body-md' - | 'body-sm' - | 'label-lg' - | 'label-md' - | 'label-sm'; - -/** - * Text component props - */ -export interface TextProps { - /** - * Typography variant determining size and font family - */ - variant: TypographyVariant; - - /** - * Text color from design system color tokens - */ - color?: keyof ColorTokens; - - /** - * Font weight from design system typography tokens - */ - weight?: keyof TypographyTokens['weights']; - - /** - * Text alignment - */ - align?: 'left' | 'center' | 'right'; - - /** - * Transform text to uppercase - */ - uppercase?: boolean; - - /** - * Text content - */ - children: React.ReactNode; - - /** - * Additional custom styles - */ - style?: StyleProp; -} - -/** - * Maps variant type to font family category - */ -function getFontFamilyForVariant(variant: TypographyVariant): keyof TypographyTokens['fonts'] { - // Display and headline variants use Manrope - if (variant.startsWith('display-') || variant.startsWith('headline-')) { - return 'headline'; - } - - // Body and title variants use Inter - if (variant.startsWith('body-') || variant.startsWith('title-')) { - return 'body'; - } - - // Label variants use Space Grotesk - if (variant.startsWith('label-')) { - return 'label'; - } - - // Default to body font - return 'body'; -} - -/** - * Maps variant to font size token key - */ -function getFontSizeKey(variant: TypographyVariant): keyof TypographyTokens['sizes'] { - // Convert variant format (e.g., 'display-lg') to camelCase (e.g., 'displayLg') - const parts = variant.split('-'); - const camelCase = parts[0] + parts[1].charAt(0).toUpperCase() + parts[1].slice(1); - return camelCase as keyof TypographyTokens['sizes']; -} - -/** - * Text component with design system typography variants. - * - * Automatically applies the correct font family based on variant: - * - display-* and headline-* variants use Manrope - * - body-* and title-* variants use Inter - * - label-* variants use Space Grotesk - * - * @example - * ```tsx - * - * Welcome to CodeLink - * - * - * - * This is body text with default styling - * - * - * - * Metadata Label - * - * ``` - */ -export const Text: React.FC = ({ - variant, - color = 'onSurface', - weight = 'regular', - align = 'left', - uppercase = false, - children, - style, -}) => { - const { theme } = useDesignSystem(); - - // Get font family based on variant type - const fontFamilyCategory = getFontFamilyForVariant(variant); - const fontFamily = theme.typography.fonts[fontFamilyCategory]; - - // Get font size from variant - const fontSizeKey = getFontSizeKey(variant); - const baseFontSize = theme.typography.sizes[fontSizeKey]; - - // Apply system font scale for accessibility (Requirement 14.9) - // This respects iOS Dynamic Type and Android Font Size settings - const fontScale = PixelRatio.getFontScale(); - const fontSize = baseFontSize * fontScale; - - // Get font weight - const fontWeight = theme.typography.weights[weight]; - - // Get text color - const textColor = theme.colors[color]; - - // Compose text style - const textStyle: TextStyle = { - fontFamily, - fontSize, - fontWeight: String(fontWeight) as TextStyle['fontWeight'], - color: textColor, - textAlign: align, - textTransform: uppercase ? 'uppercase' : 'none', - }; - - return {children}; -}; diff --git a/packages/mobile-client/src/design-system/typography/fontLoading.ts b/packages/mobile-client/src/design-system/typography/fontLoading.ts deleted file mode 100644 index 8a992b2..0000000 --- a/packages/mobile-client/src/design-system/typography/fontLoading.ts +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Font Loading Utility - * - * This module provides font loading functionality with error handling - * and fallback to system fonts. - * - * Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 - */ - -import { useFonts } from 'expo-font'; -import { - Manrope_400Regular, - Manrope_600SemiBold, - Manrope_700Bold, - Manrope_800ExtraBold, -} from '@expo-google-fonts/manrope'; -import { Inter_400Regular, Inter_500Medium, Inter_600SemiBold } from '@expo-google-fonts/inter'; -import { - SpaceGrotesk_400Regular, - SpaceGrotesk_500Medium, - SpaceGrotesk_700Bold, -} from '@expo-google-fonts/space-grotesk'; - -/** - * Font loading result - */ -export interface FontLoadingResult { - fontsLoaded: boolean; - fontError: Error | null; -} - -/** - * Custom hook for loading all required fonts - * - * Loads Manrope (weights 400, 600, 700, 800), Inter (weights 400, 500, 600), - * and Space Grotesk (weights 400, 500, 700) fonts. - * - * @returns Object containing fontsLoaded boolean and fontError - * - * @example - * ```tsx - * const { fontsLoaded, fontError } = useCustomFonts(); - * - * if (fontError) { - * console.error('Font loading failed:', fontError); - * // App will continue with system fonts - * } - * - * if (!fontsLoaded) { - * return ; - * } - * ``` - */ -export function useCustomFonts(): FontLoadingResult { - const [fontsLoaded, fontError] = useFonts({ - // Manrope fonts (for headlines) - Manrope_400Regular, - Manrope_600SemiBold, - Manrope_700Bold, - Manrope_800ExtraBold, - - // Inter fonts (for body text) - Inter_400Regular, - Inter_500Medium, - Inter_600SemiBold, - - // Space Grotesk fonts (for labels) - SpaceGrotesk_400Regular, - SpaceGrotesk_500Medium, - SpaceGrotesk_700Bold, - }); - - // Log error if font loading fails - if (fontError) { - console.error('Font loading failed:', fontError); - console.warn('Falling back to system fonts'); - } - - return { - fontsLoaded, - fontError, - }; -} - -/** - * Get font family name based on variant and weight - * - * Returns the appropriate font family string for React Native StyleSheet. - * Falls back to system fonts if custom fonts are not loaded. - * - * @param variant - Font variant: 'headline', 'body', 'label', or 'mono' - * @param weight - Font weight: 400, 500, 600, 700, or 800 - * @param fontsLoaded - Whether custom fonts are loaded - * @returns Font family string - */ -export function getFontFamily( - variant: 'headline' | 'body' | 'label' | 'mono', - weight: 400 | 500 | 600 | 700 | 800, - fontsLoaded: boolean -): string { - // Fallback to system fonts if custom fonts not loaded - if (!fontsLoaded) { - if (variant === 'mono') { - return 'monospace'; - } - return 'System'; - } - - // Return appropriate font family based on variant and weight - switch (variant) { - case 'headline': - switch (weight) { - case 400: - return 'Manrope_400Regular'; - case 600: - return 'Manrope_600SemiBold'; - case 700: - return 'Manrope_700Bold'; - case 800: - return 'Manrope_800ExtraBold'; - default: - return 'Manrope_400Regular'; - } - - case 'body': - switch (weight) { - case 400: - return 'Inter_400Regular'; - case 500: - return 'Inter_500Medium'; - case 600: - return 'Inter_600SemiBold'; - default: - return 'Inter_400Regular'; - } - - case 'label': - switch (weight) { - case 400: - return 'SpaceGrotesk_400Regular'; - case 500: - return 'SpaceGrotesk_500Medium'; - case 700: - return 'SpaceGrotesk_700Bold'; - default: - return 'SpaceGrotesk_400Regular'; - } - - case 'mono': - return 'monospace'; - - default: - return 'System'; - } -} diff --git a/packages/mobile-client/src/design-system/typography/index.ts b/packages/mobile-client/src/design-system/typography/index.ts deleted file mode 100644 index e5d8880..0000000 --- a/packages/mobile-client/src/design-system/typography/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Design System Typography - * - * This module exports typography components and utilities - * for the Obsidian UI design system. - */ - -export * from './Text'; -export * from './fontLoading'; -export * from './DynamicTextExample'; diff --git a/packages/mobile-client/src/hooks/index.ts b/packages/mobile-client/src/hooks/index.ts deleted file mode 100644 index a244e0b..0000000 --- a/packages/mobile-client/src/hooks/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Custom React hooks -// This file will export all custom hooks - -export { - ConnectionStatusProvider, - useConnection, - type ConnectionStatus, - type ConnectionContextValue, - type ConnectionStatusProviderProps, -} from './useConnection'; - -export { useOrientation, type Orientation, type UseOrientationResult } from './useOrientation'; - -export { ThemeProvider, useTheme } from './useTheme'; - -export { usePromptHistory, type PromptHistoryItem } from './usePromptHistory'; - -export { useDraftPrompt } from './useDraftPrompt'; - -export { - useConnectionQuality, - type ConnectionQuality, - type ConnectionMetrics, -} from './useConnectionQuality'; - -export { - useScreenReaderAnnouncement, - useScreenChangeAnnouncement, - useLoadingAnnouncement, -} from './useScreenReaderAnnouncement'; - -export { usePlatformNavigation } from './usePlatformNavigation'; - -export { useResponsiveLayout, type ResponsiveLayoutConfig } from './useResponsiveLayout'; diff --git a/packages/mobile-client/src/hooks/useConnection.tsx b/packages/mobile-client/src/hooks/useConnection.tsx deleted file mode 100644 index 0440a1a..0000000 --- a/packages/mobile-client/src/hooks/useConnection.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import React, { createContext, useContext, useEffect, useRef, useState, ReactNode } from 'react'; -import { AccessibilityInfo } from 'react-native'; -import { SocketManager, SocketManagerImpl } from '../services/SocketManager'; - -/** - * Connection status type - */ -export type ConnectionStatus = 'connected' | 'disconnected' | 'connecting'; - -/** - * Connection context value interface - */ -export interface ConnectionContextValue { - status: ConnectionStatus; - error: Error | null; - reconnect: () => void; - socketManager: SocketManager; -} - -/** - * Connection context - */ -const ConnectionContext = createContext(null); - -/** - * ConnectionStatusProvider props - */ -export interface ConnectionStatusProviderProps { - children: ReactNode; - serverUrl?: string; -} - -/** - * Default relay server URL - */ -// const DEFAULT_SERVER_URL = process.env.RELAY_SERVER_URL || 'http://localhost:8080'; -const DEFAULT_SERVER_URL = 'http://localhost:8080'; - -/** - * ConnectionStatusProvider manages global connection state and provides - * access to the SocketManager instance for the entire application - */ -export const ConnectionStatusProvider: React.FC = ({ - children, - serverUrl = DEFAULT_SERVER_URL, -}) => { - const [status, setStatus] = useState('disconnected'); - const [error, setError] = useState(null); - const socketManager = useRef(new SocketManagerImpl()); - const serverUrlRef = useRef(serverUrl); - - useEffect(() => { - const manager = socketManager.current; - - // Register event handlers - manager.onConnect(() => { - setStatus('connected'); - setError(null); - // Requirement 14.11: Announce connection state changes - AccessibilityInfo.announceForAccessibility('Connected to relay server'); - }); - - manager.onDisconnect(() => { - setStatus('disconnected'); - // Requirement 14.11: Announce connection state changes - AccessibilityInfo.announceForAccessibility('Disconnected from relay server'); - }); - - manager.onError((err) => { - // Log network errors (Requirement 17.11) - console.error('Connection error:', { - error: err.message, - stack: err.stack, - timestamp: new Date().toISOString(), - serverUrl: serverUrlRef.current, - }); - setError(err); - setStatus('disconnected'); - }); - - // Initial connection - setStatus('connecting'); - // Requirement 14.11: Announce loading states - AccessibilityInfo.announceForAccessibility('Connecting to relay server'); - manager.connect(serverUrlRef.current).catch((err) => { - // Log network errors (Requirement 17.11) - console.error('Initial connection failed:', { - error: err.message, - stack: err.stack, - timestamp: new Date().toISOString(), - serverUrl: serverUrlRef.current, - }); - setError(err); - setStatus('disconnected'); - }); - - // Cleanup on unmount - return () => { - manager.disconnect(); - }; - }, []); - - /** - * Manually trigger reconnection - */ - const reconnect = () => { - setStatus('connecting'); - setError(null); - // Requirement 14.11: Announce loading states - AccessibilityInfo.announceForAccessibility('Reconnecting to relay server'); - socketManager.current.connect(serverUrlRef.current).catch((err) => { - // Log network errors (Requirement 17.11) - console.error('Reconnection failed:', { - error: err.message, - stack: err.stack, - timestamp: new Date().toISOString(), - serverUrl: serverUrlRef.current, - }); - setError(err); - setStatus('disconnected'); - }); - }; - - const value: ConnectionContextValue = { - status, - error, - reconnect, - socketManager: socketManager.current, - }; - - return {children}; -}; - -/** - * Hook to access connection context - * @throws Error if used outside ConnectionStatusProvider - */ -export const useConnection = (): ConnectionContextValue => { - const context = useContext(ConnectionContext); - if (!context) { - throw new Error('useConnection must be used within ConnectionStatusProvider'); - } - return context; -}; diff --git a/packages/mobile-client/src/hooks/useConnectionQuality.tsx b/packages/mobile-client/src/hooks/useConnectionQuality.tsx deleted file mode 100644 index be1d17c..0000000 --- a/packages/mobile-client/src/hooks/useConnectionQuality.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Connection quality monitoring hook - * Measures latency and connection stability - */ - -import { useState, useEffect, useRef } from 'react'; -import { useConnection } from './useConnection'; - -export type ConnectionQuality = 'excellent' | 'good' | 'fair' | 'poor' | 'offline'; - -export interface ConnectionMetrics { - quality: ConnectionQuality; - latency: number | null; - lastPingTime: Date | null; -} - -export const useConnectionQuality = () => { - const { status, socketManager } = useConnection(); - const [metrics, setMetrics] = useState({ - quality: 'offline', - latency: null, - lastPingTime: null, - }); - const pingIntervalRef = useRef(undefined); - - useEffect(() => { - if (status !== 'connected') { - setMetrics({ - quality: 'offline', - latency: null, - lastPingTime: null, - }); - if (pingIntervalRef.current) { - clearInterval(pingIntervalRef.current); - } - return; - } - - // Measure latency periodically - const measureLatency = async () => { - const startTime = Date.now(); - - try { - // Send a ping message and wait for response - // This is a simplified version - you'd need to implement actual ping/pong - const latency = Date.now() - startTime; - - const quality = getQualityFromLatency(latency); - - setMetrics({ - quality, - latency, - lastPingTime: new Date(), - }); - } catch (error) { - // Log network errors (Requirement 17.11) - console.error('Connection quality check failed:', { - error: error instanceof Error ? error.message : String(error), - timestamp: new Date().toISOString(), - }); - setMetrics({ - quality: 'poor', - latency: null, - lastPingTime: new Date(), - }); - } - }; - - // Initial measurement - measureLatency(); - - // Measure every 10 seconds - pingIntervalRef.current = setInterval(measureLatency, 10000); - - return () => { - if (pingIntervalRef.current) { - clearInterval(pingIntervalRef.current); - } - }; - }, [status, socketManager]); - - return metrics; -}; - -function getQualityFromLatency(latency: number): ConnectionQuality { - if (latency < 50) return 'excellent'; - if (latency < 150) return 'good'; - if (latency < 300) return 'fair'; - return 'poor'; -} diff --git a/packages/mobile-client/src/hooks/useDraftPrompt.tsx b/packages/mobile-client/src/hooks/useDraftPrompt.tsx deleted file mode 100644 index 056b50a..0000000 --- a/packages/mobile-client/src/hooks/useDraftPrompt.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Draft prompt management hook - * Auto-saves and restores draft prompts - */ - -import { useState, useEffect } from 'react'; -import AsyncStorage from '@react-native-async-storage/async-storage'; - -const DRAFT_STORAGE_KEY = '@codelink/draft-prompt'; -const AUTO_SAVE_DELAY = 1000; // 1 second - -export const useDraftPrompt = () => { - const [draft, setDraft] = useState(''); - const [isSaving, setIsSaving] = useState(false); - const [lastSaved, setLastSaved] = useState(null); - - // Load draft on mount - useEffect(() => { - loadDraft(); - }, []); - - // Auto-save draft with debounce - useEffect(() => { - if (draft === '') return; - - const timer = setTimeout(() => { - saveDraft(draft); - }, AUTO_SAVE_DELAY); - - return () => clearTimeout(timer); - }, [draft]); - - const loadDraft = async () => { - try { - const stored = await AsyncStorage.getItem(DRAFT_STORAGE_KEY); - if (stored) { - setDraft(stored); - } - } catch (error) { - console.error('Failed to load draft:', error); - } - }; - - const saveDraft = async (text: string) => { - try { - setIsSaving(true); - await AsyncStorage.setItem(DRAFT_STORAGE_KEY, text); - setLastSaved(new Date()); - } catch (error) { - console.error('Failed to save draft:', error); - } finally { - setIsSaving(false); - } - }; - - const clearDraft = async () => { - try { - await AsyncStorage.removeItem(DRAFT_STORAGE_KEY); - setDraft(''); - setLastSaved(null); - } catch (error) { - console.error('Failed to clear draft:', error); - } - }; - - return { - draft, - setDraft, - clearDraft, - isSaving, - lastSaved, - }; -}; diff --git a/packages/mobile-client/src/hooks/useOrientation.tsx b/packages/mobile-client/src/hooks/useOrientation.tsx deleted file mode 100644 index d71ba30..0000000 --- a/packages/mobile-client/src/hooks/useOrientation.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useState, useEffect } from 'react'; -import { Dimensions, ScaledSize } from 'react-native'; - -/** - * Orientation type - */ -export type Orientation = 'portrait' | 'landscape'; - -/** - * Hook return type - */ -export interface UseOrientationResult { - orientation: Orientation; - isPortrait: boolean; - isLandscape: boolean; - width: number; - height: number; -} - -/** - * Custom hook to track device orientation - * Listens to dimension changes and determines current orientation - * - * Requirements: 10.1, 10.2, 10.3 - * - * @returns Current orientation state and dimensions - */ -export const useOrientation = (): UseOrientationResult => { - const [dimensions, setDimensions] = useState(() => Dimensions.get('window')); - - useEffect(() => { - const handleChange = ({ window }: { window: ScaledSize }) => { - setDimensions(window); - }; - - const subscription = Dimensions.addEventListener('change', handleChange); - - return () => { - subscription?.remove(); - }; - }, []); - - const { width, height } = dimensions; - const orientation: Orientation = width > height ? 'landscape' : 'portrait'; - - return { - orientation, - isPortrait: orientation === 'portrait', - isLandscape: orientation === 'landscape', - width, - height, - }; -}; diff --git a/packages/mobile-client/src/hooks/usePlatformNavigation.tsx b/packages/mobile-client/src/hooks/usePlatformNavigation.tsx deleted file mode 100644 index 93e6a3f..0000000 --- a/packages/mobile-client/src/hooks/usePlatformNavigation.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Platform-specific navigation configuration hook - * - * Requirements: - * - 26.5: Platform-specific navigation gestures - * - 26.6: iOS swipe-back gesture support - * - 26.7: Android hardware back button support - */ - -import { useMemo } from 'react'; -import type { StackNavigationOptions } from '@react-navigation/stack'; -import { supportsSwipeBack, getNavigationGestureConfig } from '../utils/platformAdaptations'; - -/** - * Hook to get platform-specific navigation options - * - * @returns Navigation options configured for the current platform - */ -export const usePlatformNavigation = (): Partial => { - const navigationOptions = useMemo(() => { - const gestureConfig = getNavigationGestureConfig(); - - return { - // Requirement 26.6: iOS swipe-back gesture support - gestureEnabled: supportsSwipeBack() ? gestureConfig.gestureEnabled : false, - gestureDirection: gestureConfig.gestureDirection, - - // Screen transition animations (Requirements 12.1, 12.2) - animation: 'slide_from_right' as const, - animationDuration: 300, - animationTypeForReplace: 'push' as const, - - // Header configuration - headerShown: false, // We use custom TopAppBar - }; - }, []); - - return navigationOptions; -}; diff --git a/packages/mobile-client/src/hooks/usePromptHistory.tsx b/packages/mobile-client/src/hooks/usePromptHistory.tsx deleted file mode 100644 index 07bb715..0000000 --- a/packages/mobile-client/src/hooks/usePromptHistory.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Prompt history management hook - * Stores and retrieves prompt history with AsyncStorage - */ - -import { useState, useEffect } from 'react'; -import AsyncStorage from '@react-native-async-storage/async-storage'; - -const HISTORY_STORAGE_KEY = '@codelink/prompt-history'; -const MAX_HISTORY_ITEMS = 50; - -export interface PromptHistoryItem { - id: string; - prompt: string; - timestamp: number; - success?: boolean; - editorUsed?: string; -} - -export const usePromptHistory = () => { - const [history, setHistory] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - // Load history on mount - useEffect(() => { - loadHistory(); - }, []); - - const loadHistory = async () => { - try { - const stored = await AsyncStorage.getItem(HISTORY_STORAGE_KEY); - if (stored) { - setHistory(JSON.parse(stored)); - } - } catch (error) { - console.error('Failed to load prompt history:', error); - } finally { - setIsLoading(false); - } - }; - - const saveHistory = async (newHistory: PromptHistoryItem[]) => { - try { - await AsyncStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(newHistory)); - setHistory(newHistory); - } catch (error) { - console.error('Failed to save prompt history:', error); - } - }; - - const addToHistory = async (item: PromptHistoryItem) => { - const newHistory = [item, ...history].slice(0, MAX_HISTORY_ITEMS); - await saveHistory(newHistory); - }; - - const updateHistoryItem = async (id: string, updates: Partial) => { - const newHistory = history.map((item) => (item.id === id ? { ...item, ...updates } : item)); - await saveHistory(newHistory); - }; - - const clearHistory = async () => { - try { - await AsyncStorage.removeItem(HISTORY_STORAGE_KEY); - setHistory([]); - } catch (error) { - console.error('Failed to clear prompt history:', error); - } - }; - - const deleteHistoryItem = async (id: string) => { - const newHistory = history.filter((item) => item.id !== id); - await saveHistory(newHistory); - }; - - return { - history, - isLoading, - addToHistory, - updateHistoryItem, - clearHistory, - deleteHistoryItem, - }; -}; diff --git a/packages/mobile-client/src/hooks/useResponsiveLayout.tsx b/packages/mobile-client/src/hooks/useResponsiveLayout.tsx deleted file mode 100644 index 9333bc6..0000000 --- a/packages/mobile-client/src/hooks/useResponsiveLayout.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Responsive Layout Hook - * - * Combines orientation detection with responsive layout utilities - * to provide a complete responsive layout solution. - * - * Requirements: - * - 13.1: Support portrait orientation on all screens - * - 13.2: Support landscape orientation on all screens - * - 13.3: Use maximum content width of 1024px on large screens - * - 13.4: Center content horizontally on wide screens - * - 13.5: Use responsive grid layouts - * - 13.6: Adjust bento grid layout based on orientation - * - 13.7: Adjust typography scales based on screen size - * - 13.9: Re-layout content smoothly on orientation change - */ - -import { useMemo } from 'react'; -import { useOrientation } from './useOrientation'; -import { - getScreenSize, - getGridColumns, - getBentoGridConfig, - getContentWidth, - getTypographyScale, - getResponsivePadding, - isLargeScreen, - isSmallScreen, - type ScreenSize, -} from '../utils/responsiveLayout'; - -/** - * Responsive layout configuration - */ -export interface ResponsiveLayoutConfig { - // Screen information - screenSize: ScreenSize; - isLargeScreen: boolean; - isSmallScreen: boolean; - - // Orientation - orientation: 'portrait' | 'landscape'; - isPortrait: boolean; - isLandscape: boolean; - - // Dimensions - width: number; - height: number; - - // Content width - contentWidth: number; - shouldCenterContent: boolean; - contentMarginHorizontal: number; - - // Grid configuration - gridColumns: number; - - // Bento grid configuration - bentoGrid: { - columns: number; - largeCardSpan: number; - smallCardSpan: number; - gap: number; - padding: number; - }; - - // Typography scale - typographyScale: number; - - // Padding - padding: { - horizontal: number; - vertical: number; - }; -} - -/** - * Hook to get responsive layout configuration - * - * Automatically updates when screen size or orientation changes. - * Requirement 13.9: Re-layout content smoothly on orientation change - * - * @returns Responsive layout configuration - */ -export const useResponsiveLayout = (): ResponsiveLayoutConfig => { - const { orientation, isPortrait, isLandscape, width, height } = useOrientation(); - - const config = useMemo(() => { - const screenSize = getScreenSize(width); - const contentWidthConfig = getContentWidth(width); - const gridColumns = getGridColumns(screenSize, orientation); - const bentoGrid = getBentoGridConfig(screenSize, orientation); - const typographyScale = getTypographyScale(screenSize); - const padding = getResponsivePadding(screenSize); - - return { - // Screen information - screenSize, - isLargeScreen: isLargeScreen(width), - isSmallScreen: isSmallScreen(width), - - // Orientation - orientation, - isPortrait, - isLandscape, - - // Dimensions - width, - height, - - // Content width - contentWidth: contentWidthConfig.width, - shouldCenterContent: contentWidthConfig.shouldCenter, - contentMarginHorizontal: contentWidthConfig.marginHorizontal, - - // Grid configuration - gridColumns, - - // Bento grid configuration - bentoGrid, - - // Typography scale - typographyScale, - - // Padding - padding, - }; - }, [orientation, isPortrait, isLandscape, width, height]); - - return config; -}; diff --git a/packages/mobile-client/src/hooks/useScreenReaderAnnouncement.tsx b/packages/mobile-client/src/hooks/useScreenReaderAnnouncement.tsx deleted file mode 100644 index cdbf49b..0000000 --- a/packages/mobile-client/src/hooks/useScreenReaderAnnouncement.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Screen Reader Announcement Hook - * - * Provides utilities for announcing messages to screen readers (VoiceOver/TalkBack). - * Used for accessibility to announce screen changes, toast notifications, and loading states. - * - * Requirements: 14.3, 14.11 - */ - -import { useEffect, useCallback } from 'react'; -import { AccessibilityInfo } from 'react-native'; - -/** - * Hook for announcing messages to screen readers - * - * @returns announce function to trigger screen reader announcements - */ -export const useScreenReaderAnnouncement = () => { - /** - * Announce a message to screen readers - * - * @param message - The message to announce - * @param delay - Optional delay in milliseconds before announcing (default: 100ms) - */ - const announce = useCallback((message: string, delay: number = 100) => { - // Small delay to ensure the screen reader is ready - setTimeout(() => { - AccessibilityInfo.announceForAccessibility(message); - }, delay); - }, []); - - return { announce }; -}; - -/** - * Hook for announcing screen changes on navigation - * - * @param screenName - The name of the current screen - */ -export const useScreenChangeAnnouncement = (screenName: string) => { - const { announce } = useScreenReaderAnnouncement(); - - useEffect(() => { - // Announce screen change with a slight delay to ensure navigation is complete - announce(`${screenName} screen`, 300); - }, [screenName, announce]); -}; - -/** - * Hook for announcing loading states - * - * @param isLoading - Whether the loading state is active - * @param loadingMessage - The message to announce when loading starts - * @param completeMessage - Optional message to announce when loading completes - */ -export const useLoadingAnnouncement = ( - isLoading: boolean, - loadingMessage: string = 'Loading', - completeMessage?: string -) => { - const { announce } = useScreenReaderAnnouncement(); - - useEffect(() => { - if (isLoading) { - announce(loadingMessage); - } else if (completeMessage) { - announce(completeMessage); - } - }, [isLoading, loadingMessage, completeMessage, announce]); -}; diff --git a/packages/mobile-client/src/hooks/useTheme.tsx b/packages/mobile-client/src/hooks/useTheme.tsx deleted file mode 100644 index a9e4b44..0000000 --- a/packages/mobile-client/src/hooks/useTheme.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Theme management hook - * Provides theme switching and persistence - */ - -import React, { useState, useEffect, createContext, useContext, ReactNode } from 'react'; -import { useColorScheme } from 'react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { lightTheme, darkTheme, ThemeMode } from '../theme'; - -const THEME_STORAGE_KEY = '@codelink/theme'; - -interface ThemeContextValue { - theme: typeof lightTheme; - themeMode: ThemeMode; - isDark: boolean; - setThemeMode: (mode: ThemeMode) => void; -} - -const ThemeContext = createContext(null); - -export const ThemeProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const systemColorScheme = useColorScheme(); - const [themeMode, setThemeModeState] = useState('auto'); - - // Load saved theme preference - useEffect(() => { - const loadTheme = async () => { - try { - const saved = await AsyncStorage.getItem(THEME_STORAGE_KEY); - if (saved) { - setThemeModeState(saved as ThemeMode); - } - } catch (error) { - console.error('Failed to load theme preference:', error); - } - }; - loadTheme(); - }, []); - - // Determine actual theme based on mode - const isDark = themeMode === 'dark' || (themeMode === 'auto' && systemColorScheme === 'dark'); - const theme = isDark ? darkTheme : lightTheme; - - // Save theme preference - const setThemeMode = async (mode: ThemeMode) => { - try { - await AsyncStorage.setItem(THEME_STORAGE_KEY, mode); - setThemeModeState(mode); - } catch (error) { - console.error('Failed to save theme preference:', error); - } - }; - - return ( - - {children} - - ); -}; - -export const useTheme = () => { - const context = useContext(ThemeContext); - if (!context) { - throw new Error('useTheme must be used within ThemeProvider'); - } - return context; -}; diff --git a/packages/mobile-client/src/navigation/BottomNavBar.tsx b/packages/mobile-client/src/navigation/BottomNavBar.tsx deleted file mode 100644 index b1b8523..0000000 --- a/packages/mobile-client/src/navigation/BottomNavBar.tsx +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Bottom Navigation Bar Component - * - * Implements the bottom navigation bar with glassmorphism effect, - * 4 navigation items, and haptic feedback. - * - * Requirements: 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 8.10, 8.12, 11.1 - */ - -import React, { useEffect, useRef } from 'react'; -import { View, TouchableOpacity, StyleSheet, Platform, Animated } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { BlurView } from 'expo-blur'; -import * as Haptics from 'expo-haptics'; -import { useDesignSystem } from '../design-system'; -import { Text } from '../design-system/typography/Text'; -import { Icon, type IconName } from '../design-system/components/Icon'; - -/** - * Navigation item configuration - */ -interface NavItem { - key: string; - label: string; - icon: IconName; -} - -/** - * BottomNavBar props - */ -interface BottomNavBarProps { - activeRoute: string; - onNavigate: (route: string) => void; -} - -/** - * Navigation items configuration - * Requirements: 8.1, 8.2 - */ -const NAV_ITEMS: NavItem[] = [ - { key: 'Dashboard', label: 'DASHBOARD', icon: 'home' }, - { key: 'Diffs', label: 'DIFFS', icon: 'difference' }, - { key: 'Compose', label: 'COMPOSE', icon: 'terminal' }, - { key: 'Settings', label: 'SETTINGS', icon: 'settings' }, -]; - -/** - * Navigation item component with animation - * Requirements: 12.7, 12.8 - */ -const NavItem: React.FC<{ - item: NavItem; - isActive: boolean; - onPress: () => void; - theme: ReturnType['theme']; -}> = ({ item, isActive, onPress, theme }) => { - const elevationAnim = useRef(new Animated.Value(isActive ? 1 : 0)).current; - const bgColorAnim = useRef(new Animated.Value(isActive ? 1 : 0)).current; - - // Animate elevation and background color on selection change - // Requirements: 12.7, 12.8 - 200ms duration - useEffect(() => { - Animated.parallel([ - Animated.timing(elevationAnim, { - toValue: isActive ? 1 : 0, - duration: 200, - useNativeDriver: false, - }), - Animated.timing(bgColorAnim, { - toValue: isActive ? 1 : 0, - duration: 200, - useNativeDriver: false, - }), - ]).start(); - }, [isActive, elevationAnim, bgColorAnim]); - - const backgroundColor = bgColorAnim.interpolate({ - inputRange: [0, 1], - outputRange: ['transparent', theme.colors.surfaceContainerLow], - }); - - const elevation = elevationAnim.interpolate({ - inputRange: [0, 1], - outputRange: [0, 4], - }); - - return ( - - - {/* Icon */} - - - {/* Label */} - - {item.label} - - - - ); -}; - -/** - * BottomNavBar component with glassmorphism effect - * - * Features: - * - 4 navigation items (Dashboard, Diffs, Compose, Settings) - * - Material Symbols icons - * - Space Grotesk font labels (uppercase) - * - Active/inactive states with design system colors - * - Glassmorphism effect with BlurView - * - Safe area padding for notched devices - * - Haptic feedback on tap - * - Animated elevation and background color transitions - * - * Requirements: 8.1-8.12, 11.1, 12.7, 12.8 - */ -export const BottomNavBar: React.FC = ({ activeRoute, onNavigate }) => { - const { theme } = useDesignSystem(); - const insets = useSafeAreaInsets(); - - /** - * Handle navigation item press - * Provides haptic feedback and navigates to route - * Requirements: 8.11, 8.12 - */ - const handlePress = async (route: string) => { - // Haptic feedback on tap - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - onNavigate(route); - }; - - return ( - - {/* Glassmorphism backdrop with expo-blur (Requirements 11.1, 11.4, 11.5) */} - - - - {NAV_ITEMS.map((item) => { - const isActive = activeRoute === item.key; - - return ( - handlePress(item.key)} - theme={theme} - /> - ); - })} - - - ); -}; - -/** - * Styles for BottomNavBar - * Requirements: 8.7, 8.8, 8.9, 8.10, 12.7, 12.8 - */ -const styles = StyleSheet.create({ - container: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - paddingTop: 8, - paddingHorizontal: 8, - // Glassmorphism effect (requires expo-blur) - // backdrop-filter: blur(20px) - handled by BlurView - ...Platform.select({ - ios: { - shadowColor: '#000', - shadowOffset: { width: 0, height: -2 }, - shadowOpacity: 0.1, - shadowRadius: 8, - }, - android: { - elevation: 8, - }, - }), - }, - itemsContainer: { - flexDirection: 'row', - justifyContent: 'space-around', - alignItems: 'center', - }, - navItem: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - minHeight: 56, // Ensures 44pt+ touch target with padding - minWidth: 64, - }, - navItemContent: { - alignItems: 'center', - justifyContent: 'center', - paddingVertical: 8, - paddingHorizontal: 4, - width: '100%', - // Shadow for iOS elevation animation - ...Platform.select({ - ios: { - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowRadius: 4, - }, - }), - }, -}); diff --git a/packages/mobile-client/src/navigation/BottomTabNavigator.tsx b/packages/mobile-client/src/navigation/BottomTabNavigator.tsx deleted file mode 100644 index fa4cecf..0000000 --- a/packages/mobile-client/src/navigation/BottomTabNavigator.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Bottom Tab Navigator - * - * Configures bottom tab navigation with screen transition animations - * and design system integration. - * - * Requirements: 12.1, 12.2 - */ - -import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; -import type { BottomTabNavigationOptions } from '@react-navigation/bottom-tabs'; - -/** - * Bottom tab navigator instance - */ -const Tab = createBottomTabNavigator(); - -/** - * Default screen options for bottom tab navigator. - * Configures screen transition animations with 300ms duration and ease-in-out timing. - * - * Requirements: 12.1, 12.2 - */ -export const defaultScreenOptions: BottomTabNavigationOptions = { - // Animation configuration - animation: 'shift', // Smooth shift animation for tab transitions - - // Header configuration (will be customized per screen) - headerShown: false, - - // Tab bar configuration (will be customized with custom component) - tabBarHideOnKeyboard: true, -}; - -/** - * Screen transition configuration - * 300ms duration with ease-in-out timing function - */ -export const transitionConfig = { - animation: 'timing' as const, - config: { - duration: 300, - easing: (t: number) => { - // Ease-in-out cubic bezier approximation - return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; - }, - }, -}; - -/** - * Export the Tab navigator for use in app - */ -export { Tab }; diff --git a/packages/mobile-client/src/navigation/NavigationContainer.tsx b/packages/mobile-client/src/navigation/NavigationContainer.tsx deleted file mode 100644 index 7fa9442..0000000 --- a/packages/mobile-client/src/navigation/NavigationContainer.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Navigation Container with Theme Integration - * - * Configures React Navigation with design system theme integration - * and custom screen transition animations. - * - * Requirements: 12.1, 12.2 - */ - -import React, { type ReactNode } from 'react'; -import { NavigationContainer as RNNavigationContainer, type Theme } from '@react-navigation/native'; -import { useDesignSystem } from '../design-system'; - -/** - * Props for NavigationContainer - */ -interface NavigationContainerProps { - children: ReactNode; -} - -/** - * Creates a React Navigation theme from the design system theme. - * Maps design system color tokens to React Navigation theme structure. - */ -function createNavigationTheme( - designSystemTheme: ReturnType['theme'] -): Theme { - return { - dark: true, // Always use dark theme as per requirements - colors: { - primary: designSystemTheme.colors.primary, - background: designSystemTheme.colors.surface, - card: designSystemTheme.colors.surfaceContainer, - text: designSystemTheme.colors.onSurface, - border: designSystemTheme.colors.outlineVariant, - notification: designSystemTheme.colors.secondary, - }, - fonts: { - regular: { - fontFamily: designSystemTheme.typography.fonts.body, - fontWeight: '400', - }, - medium: { - fontFamily: designSystemTheme.typography.fonts.body, - fontWeight: '500', - }, - bold: { - fontFamily: designSystemTheme.typography.fonts.body, - fontWeight: '700', - }, - heavy: { - fontFamily: designSystemTheme.typography.fonts.body, - fontWeight: '800', - }, - }, - }; -} - -/** - * NavigationContainer component wraps React Navigation's NavigationContainer - * with design system theme integration. - * - * Automatically syncs navigation theme with design system theme changes. - */ -export const NavigationContainer: React.FC = ({ children }) => { - const { theme } = useDesignSystem(); - - // Create navigation theme from design system theme - const navigationTheme = createNavigationTheme(theme); - - return {children}; -}; diff --git a/packages/mobile-client/src/navigation/README.md b/packages/mobile-client/src/navigation/README.md deleted file mode 100644 index fa33c7e..0000000 --- a/packages/mobile-client/src/navigation/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# Navigation Setup - -This directory contains the React Navigation configuration for the Obsidian UI redesign. - -## Components - -### NavigationContainer - -Wraps React Navigation's NavigationContainer with design system theme integration. Automatically syncs navigation theme colors with the design system. - -### BottomTabNavigator - -Configures the bottom tab navigator with: - -- Screen transition animations (300ms duration, ease-in-out timing) -- Design system integration -- Keyboard handling - -### BottomNavBar - -Custom bottom navigation bar component with: - -- 4 navigation items (Dashboard, Diffs, Compose, Settings) -- Material Symbols icons -- Space Grotesk font labels (uppercase) -- Active/inactive states with design system colors -- Glassmorphism effect (requires expo-blur) -- Safe area padding for notched devices -- Haptic feedback on tap - -## Installation - -### Required Dependencies - -All React Navigation dependencies are already installed. To enable glassmorphism effects, install expo-blur: - -```bash -npx expo install expo-blur -``` - -## Usage - -### Basic Setup - -```typescript -import { NavigationContainer, Tab, defaultScreenOptions, BottomNavBar } from './navigation'; - -function App() { - return ( - - - ( - props.navigation.navigate(route)} - /> - )} - > - - - - - - - - ); -} -``` - -### Enabling Glassmorphism - -After installing expo-blur, uncomment the BlurView in BottomNavBar.tsx: - -```typescript -import { BlurView } from 'expo-blur'; - -// In the component: - -``` - -## Configuration - -### Screen Transitions - -- Duration: 300ms -- Timing: ease-in-out cubic bezier -- Animation: shift (smooth tab transitions) - -### Theme Integration - -Navigation theme automatically syncs with design system theme: - -- Primary color β†’ navigation primary -- Surface β†’ navigation background -- Surface container β†’ navigation card -- On surface β†’ navigation text -- Outline variant β†’ navigation border -- Secondary β†’ navigation notification - -### Bottom Navigation Bar - -- Active state: secondary color (#61dac1) with surfaceContainerLow background -- Inactive state: surfaceContainerHighest color (#353535) -- Glassmorphism: 80% opacity with 20px blur (requires expo-blur) -- Touch targets: Minimum 56pt height (exceeds 44pt requirement) -- Safe area: Automatic padding for devices with bottom notch - -## Requirements - -- @react-navigation/native: ^7.2.2 -- @react-navigation/bottom-tabs: ^7.15.9 -- react-native-safe-area-context: ^5.6.2 -- react-native-screens: ^4.24.0 -- expo-haptics: ~15.0.8 -- expo-blur: (optional, for glassmorphism) - -All dependencies except expo-blur are already installed in package.json. diff --git a/packages/mobile-client/src/navigation/TopAppBar.README.md b/packages/mobile-client/src/navigation/TopAppBar.README.md deleted file mode 100644 index 92e8a27..0000000 --- a/packages/mobile-client/src/navigation/TopAppBar.README.md +++ /dev/null @@ -1,169 +0,0 @@ -# TopAppBar Component - -The TopAppBar component provides a consistent header across all screens with CodeLink branding and real-time connection status. - -## Features - -- **Branding**: Terminal icon in primary color (#95ccff) with "CodeLink" text in Manrope font -- **Connection Status**: Real-time status indicator with color-coded dots - - Green: Connected - - Red: Disconnected - - Orange: Connecting (with pulse animation) -- **Sticky Positioning**: Remains at top during scrolling -- **Safe Area Support**: Automatically adjusts for device notches and status bars -- **Accessibility**: Full screen reader support with proper labels - -## Requirements - -Implements requirements: 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8 - -## Usage - -### Basic Usage - -```tsx -import { TopAppBar } from '../navigation'; -import { useConnection } from '../hooks'; - -export const MyScreen = () => { - const { status } = useConnection(); - - return ( - - - {/* Screen content */} - - ); -}; -``` - -### With Different Connection States - -```tsx -// Connected state - - -// Disconnected state - - -// Connecting state (with pulse animation) - -``` - -### Integration with Screen Components - -The TopAppBar should be placed at the top of each screen component: - -```tsx -import React from 'react'; -import { View, ScrollView, StyleSheet } from 'react-native'; -import { TopAppBar } from '../navigation'; -import { useConnection } from '../hooks'; - -export const DashboardScreen = () => { - const { status } = useConnection(); - - return ( - - - {/* Dashboard content */} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - content: { - flex: 1, - }, -}); -``` - -## Props - -### `connectionStatus` (required) - -Type: `'connected' | 'disconnected' | 'connecting'` - -The current connection status. This determines the color and animation of the status indicator: - -- `'connected'`: Green dot with "Connected" label -- `'disconnected'`: Red dot with "Disconnected" label -- `'connecting'`: Orange dot with pulse animation and "Connecting" label - -### `showBackButton` (optional) - -Type: `boolean` -Default: `false` - -Reserved for future use. Will show a back button in the app bar. - -### `onBackPress` (optional) - -Type: `() => void` - -Reserved for future use. Callback when back button is pressed. - -## Design System Integration - -The TopAppBar uses the following design system tokens: - -### Colors - -- Background: `theme.colors.surface` (#131313) -- Icon: `theme.colors.primary` (#95ccff) -- Text: `theme.colors.onSurface` -- Status colors: `theme.colors.secondary` (green), `theme.colors.error` (red), `theme.colors.tertiary` (orange) - -### Typography - -- Brand text: `title-lg` variant with `bold` weight (Manrope font) -- Status label: `label-md` variant with `medium` weight (Space Grotesk font) - -### Spacing - -- Horizontal padding: 16px -- Vertical padding: 12px -- Minimum height: 56px (standard app bar height) - -## Accessibility - -The TopAppBar includes proper accessibility support: - -- Status indicator has descriptive labels for screen readers -- Proper semantic structure for navigation -- High contrast support through design system tokens - -## Animation - -The connecting state includes a pulse animation: - -- Scale: 1.0 β†’ 1.5 β†’ 1.0 -- Opacity: 1.0 β†’ 0.5 β†’ 1.0 -- Duration: 1000ms per cycle -- Loops continuously while in connecting state - -## Platform Considerations - -### iOS - -- Automatically adjusts for status bar height using safe area insets -- Uses iOS-style shadow for depth - -### Android - -- Uses elevation for depth -- Respects system navigation bar - -## Testing - -See `TopAppBar.example.tsx` for usage examples and integration patterns. - -## Related Components - -- `StatusIndicator`: Used internally for connection status display -- `Icon`: Used for terminal icon -- `Text`: Used for brand text -- `BottomNavBar`: Complementary navigation component at bottom of screen diff --git a/packages/mobile-client/src/navigation/TopAppBar.example.tsx b/packages/mobile-client/src/navigation/TopAppBar.example.tsx deleted file mode 100644 index 76febf5..0000000 --- a/packages/mobile-client/src/navigation/TopAppBar.example.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/** - * TopAppBar Usage Examples - * - * This file demonstrates how to use the TopAppBar component in different scenarios. - */ - -import React from 'react'; -import { ScrollView, View, StyleSheet } from 'react-native'; -import { TopAppBar } from './TopAppBar'; -import { Text } from '../design-system/typography/Text'; - -/** - * Example 1: Basic usage with connected status - */ -export const BasicConnectedExample = () => { - return ( - - - - - This example shows the TopAppBar with a connected status. The status indicator will show a - green dot with "Connected" label. - - - - ); -}; - -/** - * Example 2: Disconnected status - */ -export const DisconnectedExample = () => { - return ( - - - - - This example shows the TopAppBar with a disconnected status. The status indicator will - show a red dot with "Disconnected" label. - - - - ); -}; - -/** - * Example 3: Connecting status with pulse animation - */ -export const ConnectingExample = () => { - return ( - - - - - This example shows the TopAppBar with a connecting status. The status indicator will show - an orange dot with pulse animation. - - - - ); -}; - -/** - * Example 4: Integration with screen component - */ -export const ScreenIntegrationExample = () => { - const [connectionStatus, setConnectionStatus] = React.useState< - 'connected' | 'disconnected' | 'connecting' - >('connecting'); - - // Simulate connection status changes - React.useEffect(() => { - const timer = setTimeout(() => { - setConnectionStatus('connected'); - }, 3000); - - return () => clearTimeout(timer); - }, []); - - return ( - - - - - Dashboard - - - This example demonstrates how to integrate the TopAppBar with a screen component. The - connection status will change from "connecting" to "connected" after 3 seconds. - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - content: { - flex: 1, - padding: 16, - }, -}); diff --git a/packages/mobile-client/src/navigation/TopAppBar.tsx b/packages/mobile-client/src/navigation/TopAppBar.tsx deleted file mode 100644 index 1b745d9..0000000 --- a/packages/mobile-client/src/navigation/TopAppBar.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Top App Bar Component - * - * Implements the top app bar with CodeLink branding and connection status indicator. - * Features sticky positioning, connection status colors, and pulse animation for connecting state. - * - * Requirements: 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8 - */ - -import React from 'react'; -import { View, StyleSheet, Platform } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useDesignSystem } from '../design-system'; -import { Text } from '../design-system/typography/Text'; -import { Icon } from '../design-system/components/Icon'; -import { StatusIndicator, ConnectionStatus } from '../design-system/components/StatusIndicator'; - -/** - * TopAppBar props - */ -export interface TopAppBarProps { - /** - * Current connection status - */ - connectionStatus: ConnectionStatus; - - /** - * Show back button (optional, for future use) - */ - showBackButton?: boolean; - - /** - * Back button press handler (optional, for future use) - */ - onBackPress?: () => void; -} - -/** - * TopAppBar component with branding and connection status - * - * Features: - * - Terminal icon in primary color (#95ccff) - * - "CodeLink" text in Manrope font - * - Connection status indicator on right side - * - Surface background color (#131313) - * - Sticky positioning at top during scrolling - * - Connection status colors (green for connected, red for disconnected) - * - Pulse animation for connecting state - * - * Requirements: 9.1-9.8 - */ -export const TopAppBar: React.FC = ({ - connectionStatus, - showBackButton: _showBackButton = false, - onBackPress: _onBackPress, -}) => { - const { theme } = useDesignSystem(); - const insets = useSafeAreaInsets(); - - return ( - - - {/* Left section: Branding */} - - {/* Terminal icon in primary color */} - - - {/* CodeLink text in Manrope font */} - - CodeLink - - - - {/* Right section: Connection status */} - - - - - - ); -}; - -/** - * Styles for TopAppBar - * Requirements: 9.4, 9.5 - */ -const styles = StyleSheet.create({ - container: { - // Sticky at top during scrolling - position: 'relative', - zIndex: 100, - // Shadow for depth - ...Platform.select({ - ios: { - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.1, - shadowRadius: 4, - }, - android: { - elevation: 4, - }, - }), - }, - content: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: 16, - paddingVertical: 12, - minHeight: 56, // Standard app bar height - }, - branding: { - flexDirection: 'row', - alignItems: 'center', - }, - brandIcon: { - marginRight: 8, - }, - brandText: { - // Manrope font applied via Text component - }, - statusContainer: { - flexDirection: 'row', - alignItems: 'center', - }, -}); diff --git a/packages/mobile-client/src/navigation/index.ts b/packages/mobile-client/src/navigation/index.ts deleted file mode 100644 index 2a51f22..0000000 --- a/packages/mobile-client/src/navigation/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Navigation Module Exports - * - * Central export point for navigation components and configuration. - */ - -export { NavigationContainer } from './NavigationContainer'; -export { Tab, defaultScreenOptions, transitionConfig } from './BottomTabNavigator'; -export { BottomNavBar } from './BottomNavBar'; -export { TopAppBar } from './TopAppBar'; -export type { TopAppBarProps } from './TopAppBar'; diff --git a/packages/mobile-client/src/navigation/types.ts b/packages/mobile-client/src/navigation/types.ts deleted file mode 100644 index 76f3dc1..0000000 --- a/packages/mobile-client/src/navigation/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Navigation Types - * - * TypeScript type definitions for navigation routes and parameters. - */ - -/** - * Bottom tab navigator route parameter list. - * Defines the available routes and their parameters. - */ -export type BottomTabParamList = { - Dashboard: undefined; - Diffs: undefined; - Compose: undefined; - Settings: undefined; -}; - -/** - * Navigation route names - */ -export type RouteNames = keyof BottomTabParamList; diff --git a/packages/mobile-client/src/services/DiffMessageHandler.ts b/packages/mobile-client/src/services/DiffMessageHandler.ts deleted file mode 100644 index 9ee5005..0000000 --- a/packages/mobile-client/src/services/DiffMessageHandler.ts +++ /dev/null @@ -1,213 +0,0 @@ -import type { SyncFullContextMessage, FileContextPayload } from '@codelink/protocol'; -import { isSyncFullContextMessage } from '../utils/messageValidation'; - -/** - * DiffState represents the current state of diff data - */ -export interface DiffState { - currentDiff: FileContextPayload | null; - history: FileContextPayload[]; - selectedIndex: number; -} - -/** - * DiffMessageHandler manages SYNC_FULL_CONTEXT message handling and diff state - */ -export class DiffMessageHandler { - private diffState: DiffState = { - currentDiff: null, - history: [], - selectedIndex: -1, - }; - private stateChangeListeners: Array<(state: DiffState) => void> = []; - private errorListeners: Array<(error: Error) => void> = []; - private maxHistorySize: number; - - constructor(maxHistorySize: number = 50) { - this.maxHistorySize = maxHistorySize; - } - - /** - * Handles incoming SYNC_FULL_CONTEXT messages - * @param message - The message to handle - * @returns true if message was handled successfully, false otherwise - */ - handleMessage(message: unknown): boolean { - try { - // Validate message type - if (!isSyncFullContextMessage(message)) { - const error = new Error('Invalid message type: expected SYNC_FULL_CONTEXT'); - this.notifyErrorListeners(error); - return false; - } - - // Parse FileContextPayload - const payload = this.parseFileContextPayload(message); - - // Update diff state - this.updateDiffState(payload); - - return true; - } catch (error) { - const err = error instanceof Error ? error : new Error('Failed to handle message'); - this.notifyErrorListeners(err); - return false; - } - } - - /** - * Parses and validates FileContextPayload from message - * @param message - SYNC_FULL_CONTEXT message - * @returns Validated FileContextPayload - * @throws Error if payload is invalid - */ - private parseFileContextPayload(message: SyncFullContextMessage): FileContextPayload { - const { payload } = message; - - // Validate required fields - if (!payload.fileName || typeof payload.fileName !== 'string') { - throw new Error('Invalid payload: fileName is required and must be a string'); - } - - if (typeof payload.originalFile !== 'string') { - throw new Error('Invalid payload: originalFile must be a string'); - } - - if (typeof payload.modifiedFile !== 'string') { - throw new Error('Invalid payload: modifiedFile must be a string'); - } - - if (typeof payload.isDirty !== 'boolean') { - throw new Error('Invalid payload: isDirty must be a boolean'); - } - - if (typeof payload.timestamp !== 'number') { - throw new Error('Invalid payload: timestamp must be a number'); - } - - return payload; - } - - /** - * Updates diff state with new payload - * @param payload - FileContextPayload to add to state - */ - private updateDiffState(payload: FileContextPayload): void { - // Add to history - const newHistory = [...this.diffState.history, payload]; - - // Trim history if it exceeds max size - if (newHistory.length > this.maxHistorySize) { - newHistory.shift(); - } - - // Update state - this.diffState = { - currentDiff: payload, - history: newHistory, - selectedIndex: newHistory.length - 1, - }; - - // Notify listeners - this.notifyStateChangeListeners(); - } - - /** - * Gets the current diff state - * @returns Current DiffState - */ - getDiffState(): DiffState { - return { ...this.diffState }; - } - - /** - * Gets the current diff payload - * @returns Current FileContextPayload or null - */ - getCurrentDiff(): FileContextPayload | null { - return this.diffState.currentDiff; - } - - /** - * Gets diff history - * @returns Array of FileContextPayload - */ - getHistory(): FileContextPayload[] { - return [...this.diffState.history]; - } - - /** - * Selects a diff from history by index - * @param index - Index in history array - * @returns true if selection was successful, false otherwise - */ - selectDiffByIndex(index: number): boolean { - if (index < 0 || index >= this.diffState.history.length) { - return false; - } - - this.diffState = { - ...this.diffState, - currentDiff: this.diffState.history[index], - selectedIndex: index, - }; - - this.notifyStateChangeListeners(); - return true; - } - - /** - * Clears diff history - */ - clearHistory(): void { - this.diffState = { - currentDiff: null, - history: [], - selectedIndex: -1, - }; - - this.notifyStateChangeListeners(); - } - - /** - * Registers a listener for state changes - * @param listener - Function to call when state changes - */ - onStateChange(listener: (state: DiffState) => void): void { - this.stateChangeListeners.push(listener); - } - - /** - * Registers a listener for errors - * @param listener - Function to call when error occurs - */ - onError(listener: (error: Error) => void): void { - this.errorListeners.push(listener); - } - - /** - * Notifies all state change listeners - */ - private notifyStateChangeListeners(): void { - this.stateChangeListeners.forEach((listener) => { - try { - listener(this.getDiffState()); - } catch (error) { - console.error('Error in state change listener:', error); - } - }); - } - - /** - * Notifies all error listeners - */ - private notifyErrorListeners(error: Error): void { - this.errorListeners.forEach((listener) => { - try { - listener(error); - } catch (err) { - console.error('Error in error listener:', err); - } - }); - } -} diff --git a/packages/mobile-client/src/services/PromptManager.ts b/packages/mobile-client/src/services/PromptManager.ts deleted file mode 100644 index d600723..0000000 --- a/packages/mobile-client/src/services/PromptManager.ts +++ /dev/null @@ -1,169 +0,0 @@ -import type { InjectPromptMessage, InjectPromptResponse } from '@codelink/protocol'; -import type { SocketManager } from './SocketManager'; - -/** - * Status of a pending prompt - */ -export type PromptStatus = 'pending' | 'success' | 'error'; - -/** - * Represents a prompt that has been submitted - */ -export interface PendingPrompt { - id: string; - prompt: string; - timestamp: number; - status: PromptStatus; -} - -/** - * Callback function for prompt responses - */ -export type ResponseCallback = (response: InjectPromptResponse) => void; - -/** - * PromptManager interface defines the contract for managing prompt submissions - */ -export interface PromptManager { - // Prompt submission - submitPrompt(prompt: string): string; // Returns message ID - - // Response handling - handleResponse(response: InjectPromptResponse): void; - - // State queries - getPendingPrompts(): PendingPrompt[]; - getPromptStatus(id: string): PendingPrompt | null; - - // Callback registration - onResponse(callback: ResponseCallback): void; -} - -/** - * PromptManagerImpl manages prompt submission, tracking, and response correlation - */ -export class PromptManagerImpl implements PromptManager { - private pendingPrompts: Map = new Map(); - private socketManager: SocketManager; - private responseCallbacks: ResponseCallback[] = []; - - constructor(socketManager: SocketManager) { - this.socketManager = socketManager; - } - - /** - * Generates a unique message ID - * @returns Unique identifier string - */ - private generateMessageId(): string { - return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`; - } - - /** - * Submits a prompt to the relay server - * @param prompt - The prompt text to submit - * @returns The message ID for tracking - * @throws Error if not connected or prompt is invalid - */ - submitPrompt(prompt: string): string { - // Validate prompt - if (!prompt || prompt.trim().length === 0) { - throw new Error('Prompt cannot be empty'); - } - - // Check connection - if (!this.socketManager.isConnected()) { - throw new Error('Cannot submit prompt: not connected to server'); - } - - // Generate unique message ID - const messageId = this.generateMessageId(); - const timestamp = Date.now(); - - // Create INJECT_PROMPT message conforming to protocol - const message: InjectPromptMessage = { - type: 'INJECT_PROMPT', - id: messageId, - timestamp, - payload: { - prompt: prompt.trim(), - }, - }; - - // Store in pending prompts map - const pendingPrompt: PendingPrompt = { - id: messageId, - prompt: prompt.trim(), - timestamp, - status: 'pending', - }; - this.pendingPrompts.set(messageId, pendingPrompt); - - // Send via socket manager - try { - this.socketManager.sendMessage(message); - } catch (error) { - // Remove from pending if send fails - this.pendingPrompts.delete(messageId); - throw error; - } - - return messageId; - } - - /** - * Handles a response from the relay server - * @param response - The INJECT_PROMPT_RESPONSE message - */ - handleResponse(response: InjectPromptResponse): void { - // Find original prompt using response.originalId - const originalPrompt = this.pendingPrompts.get(response.originalId); - - if (!originalPrompt) { - console.warn(`Received response for unknown prompt ID: ${response.originalId}`); - return; - } - - // Update prompt status - originalPrompt.status = response.payload.success ? 'success' : 'error'; - - // Invoke registered callbacks - this.responseCallbacks.forEach((callback) => { - try { - callback(response); - } catch (error) { - console.error('Error in response callback:', error); - } - }); - - // Clean up completed prompt after a delay to allow status queries - setTimeout(() => { - this.pendingPrompts.delete(response.originalId); - }, 5000); - } - - /** - * Gets all pending prompts - * @returns Array of pending prompts - */ - getPendingPrompts(): PendingPrompt[] { - return Array.from(this.pendingPrompts.values()); - } - - /** - * Gets the status of a specific prompt - * @param id - The message ID to query - * @returns The prompt status or null if not found - */ - getPromptStatus(id: string): PendingPrompt | null { - return this.pendingPrompts.get(id) || null; - } - - /** - * Registers a callback for prompt responses - * @param callback - Function to call when response is received - */ - onResponse(callback: ResponseCallback): void { - this.responseCallbacks.push(callback); - } -} diff --git a/packages/mobile-client/src/services/SocketManager.ts b/packages/mobile-client/src/services/SocketManager.ts deleted file mode 100644 index 400c701..0000000 --- a/packages/mobile-client/src/services/SocketManager.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { io, Socket } from 'socket.io-client'; -import type { ProtocolMessage } from '@codelink/protocol'; -import { getConfig } from '../config'; - -/** - * SocketManager interface defines the contract for WebSocket connection management - */ -export interface SocketManager { - // Connection management - connect(serverUrl: string): Promise; - disconnect(): void; - isConnected(): boolean; - - // Message sending - sendMessage(message: ProtocolMessage): void; - - // Event listeners - onMessage(handler: (message: ProtocolMessage) => void): void; - onConnect(handler: () => void): void; - onDisconnect(handler: () => void): void; - onError(handler: (error: Error) => void): void; -} - -/** - * SocketManagerImpl implements WebSocket connection management using Socket.IO - * with automatic reconnection and exponential backoff - */ -export class SocketManagerImpl implements SocketManager { - private socket: Socket | null = null; - private messageHandlers: Array<(message: ProtocolMessage) => void> = []; - private connectHandlers: Array<() => void> = []; - private disconnectHandlers: Array<() => void> = []; - private errorHandlers: Array<(error: Error) => void> = []; - private reconnectAttempts = 0; - private maxReconnectAttempts: number; - private baseReconnectDelay: number; - private reconnectTimer: NodeJS.Timeout | null = null; - private isManualDisconnect = false; - - constructor() { - // Load configuration values - const config = getConfig(); - this.maxReconnectAttempts = config.socketOptions.reconnectionAttempts; - this.baseReconnectDelay = config.socketOptions.reconnectionDelay; - } - - /** - * Establishes connection to the relay server - * @param serverUrl - WebSocket server URL - */ - async connect(serverUrl: string): Promise { - return new Promise((resolve, reject) => { - try { - this.isManualDisconnect = false; - - // Get configuration - const config = getConfig(); - - // Initialize Socket.IO connection with configuration - this.socket = io(serverUrl, { - reconnection: false, // We handle reconnection manually for exponential backoff - timeout: config.socketOptions.timeout, - transports: ['websocket'], - }); - - // Set up event listeners - this.socket.on('connect', () => { - this.reconnectAttempts = 0; - this.notifyConnectHandlers(); - resolve(); - }); - - this.socket.on('disconnect', (reason) => { - this.notifyDisconnectHandlers(); - - // Attempt automatic reconnection if not manually disconnected - if (!this.isManualDisconnect && reason !== 'io client disconnect') { - this.attemptReconnect(serverUrl); - } - }); - - this.socket.on('message', (data: unknown) => { - try { - // Parse JSON string from relay server - const message = JSON.parse(data as string) as ProtocolMessage; - this.notifyMessageHandlers(message); - } catch (error) { - const err = error instanceof Error ? error : new Error('Message parsing failed'); - this.notifyErrorHandlers(err); - } - }); - - this.socket.on('connect_error', (error) => { - const err = new Error(`Connection error: ${error.message}`); - this.notifyErrorHandlers(err); - - // If initial connection fails, reject the promise - if (this.reconnectAttempts === 0) { - reject(err); - } - - // Attempt reconnection - if (!this.isManualDisconnect) { - this.attemptReconnect(serverUrl); - } - }); - - this.socket.on('error', (error) => { - const err = error instanceof Error ? error : new Error('Socket error'); - this.notifyErrorHandlers(err); - }); - } catch (error) { - const err = error instanceof Error ? error : new Error('Failed to initialize socket'); - this.notifyErrorHandlers(err); - reject(err); - } - }); - } - - /** - * Attempts to reconnect with exponential backoff - * @param serverUrl - WebSocket server URL - */ - private attemptReconnect(serverUrl: string): void { - // Clear any existing reconnect timer - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer); - this.reconnectTimer = null; - } - - // Check if we've exceeded max attempts - if (this.reconnectAttempts >= this.maxReconnectAttempts) { - const error = new Error('Max reconnection attempts reached'); - this.notifyErrorHandlers(error); - return; - } - - // Calculate delay with exponential backoff - const delay = this.baseReconnectDelay * Math.pow(2, this.reconnectAttempts); - this.reconnectAttempts++; - - // Schedule reconnection attempt - this.reconnectTimer = setTimeout(() => { - if (!this.isManualDisconnect) { - this.connect(serverUrl).catch((_error) => { - // Error already handled in connect method - }); - } - }, delay); - } - - /** - * Disconnects from the relay server - */ - disconnect(): void { - this.isManualDisconnect = true; - - // Clear reconnect timer - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer); - this.reconnectTimer = null; - } - - // Disconnect socket - if (this.socket) { - this.socket.disconnect(); - this.socket = null; - } - - this.reconnectAttempts = 0; - } - - /** - * Checks if socket is currently connected - * @returns true if connected, false otherwise - */ - isConnected(): boolean { - return this.socket !== null && this.socket.connected; - } - - /** - * Sends a message to the relay server - * @param message - Protocol message to send - * @throws Error if not connected - */ - sendMessage(message: ProtocolMessage): void { - if (!this.isConnected()) { - const error = new Error('Cannot send message: not connected to server'); - this.notifyErrorHandlers(error); - throw error; - } - - try { - this.socket!.emit('message', JSON.stringify(message)); - } catch (error) { - const err = error instanceof Error ? error : new Error('Failed to send message'); - this.notifyErrorHandlers(err); - throw err; - } - } - - /** - * Registers a handler for incoming messages - * @param handler - Function to call when message is received - */ - onMessage(handler: (message: ProtocolMessage) => void): void { - this.messageHandlers.push(handler); - } - - /** - * Registers a handler for connection events - * @param handler - Function to call when connected - */ - onConnect(handler: () => void): void { - this.connectHandlers.push(handler); - } - - /** - * Registers a handler for disconnection events - * @param handler - Function to call when disconnected - */ - onDisconnect(handler: () => void): void { - this.disconnectHandlers.push(handler); - } - - /** - * Registers a handler for error events - * @param handler - Function to call when error occurs - */ - onError(handler: (error: Error) => void): void { - this.errorHandlers.push(handler); - } - - /** - * Notifies all registered message handlers - */ - private notifyMessageHandlers(message: ProtocolMessage): void { - this.messageHandlers.forEach((handler) => { - try { - handler(message); - } catch (error) { - console.error('Error in message handler:', error); - } - }); - } - - /** - * Notifies all registered connect handlers - */ - private notifyConnectHandlers(): void { - this.connectHandlers.forEach((handler) => { - try { - handler(); - } catch (error) { - console.error('Error in connect handler:', error); - } - }); - } - - /** - * Notifies all registered disconnect handlers - */ - private notifyDisconnectHandlers(): void { - this.disconnectHandlers.forEach((handler) => { - try { - handler(); - } catch (error) { - console.error('Error in disconnect handler:', error); - } - }); - } - - /** - * Notifies all registered error handlers - */ - private notifyErrorHandlers(error: Error): void { - this.errorHandlers.forEach((handler) => { - try { - handler(error); - } catch (err) { - console.error('Error in error handler:', err); - } - }); - } -} diff --git a/packages/mobile-client/src/services/index.ts b/packages/mobile-client/src/services/index.ts deleted file mode 100644 index 70f6d18..0000000 --- a/packages/mobile-client/src/services/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Service layer exports -// This file will export all service implementations - -export { SocketManagerImpl } from './SocketManager'; -export type { SocketManager } from './SocketManager'; -export { DiffMessageHandler, type DiffState } from './DiffMessageHandler'; -export { - PromptManagerImpl, - type PromptManager, - type PendingPrompt, - type PromptStatus, - type ResponseCallback, -} from './PromptManager'; diff --git a/packages/mobile-client/src/theme/index.ts b/packages/mobile-client/src/theme/index.ts deleted file mode 100644 index ce8af59..0000000 --- a/packages/mobile-client/src/theme/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Theme configuration for the mobile client - * Supports light and dark modes with Material Design 3 - */ - -import { MD3LightTheme, MD3DarkTheme } from 'react-native-paper'; - -export const lightTheme = { - ...MD3LightTheme, - colors: { - ...MD3LightTheme.colors, - primary: '#6200ee', - secondary: '#03dac6', - error: '#b00020', - background: '#ffffff', - surface: '#f5f5f5', - surfaceVariant: '#e7e0ec', - onSurface: '#1c1b1f', - onSurfaceVariant: '#49454f', - }, -}; - -export const darkTheme = { - ...MD3DarkTheme, - colors: { - ...MD3DarkTheme.colors, - primary: '#BB86FC', - secondary: '#03DAC6', - error: '#CF6679', - background: '#121212', - surface: '#1E1E1E', - surfaceVariant: '#2d2d2d', - onSurface: '#e1e1e1', - onSurfaceVariant: '#c7c7c7', - }, -}; - -export type ThemeMode = 'light' | 'dark' | 'auto'; diff --git a/packages/mobile-client/src/types/index.ts b/packages/mobile-client/src/types/index.ts deleted file mode 100644 index 085463e..0000000 --- a/packages/mobile-client/src/types/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type ConnectionStatus = 'connected' | 'disconnected' | 'connecting'; - -export interface ConnectionState { - status: ConnectionStatus; - error: Error | null; - lastConnectedAt: number | null; - reconnectAttempts: number; -} diff --git a/packages/mobile-client/src/utils/errorHandling.ts b/packages/mobile-client/src/utils/errorHandling.ts deleted file mode 100644 index 8931217..0000000 --- a/packages/mobile-client/src/utils/errorHandling.ts +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Error handling utilities for the mobile client - * Provides error message formatting, display helpers, and logging - * Validates: Requirements 9.1, 9.2, 9.3, 9.4 - */ - -/** - * Error types that can occur in the mobile client - */ -export enum ErrorType { - NETWORK_ERROR = 'NETWORK_ERROR', - PROMPT_SUBMISSION_ERROR = 'PROMPT_SUBMISSION_ERROR', - CONNECTION_ERROR = 'CONNECTION_ERROR', - UNEXPECTED_ERROR = 'UNEXPECTED_ERROR', - VALIDATION_ERROR = 'VALIDATION_ERROR', - PARSING_ERROR = 'PARSING_ERROR', -} - -/** - * Structured error information - */ -export interface AppError { - type: ErrorType; - message: string; - originalError?: Error | unknown; - actionableSteps?: string[]; - timestamp: number; -} - -/** - * Format an error into a user-friendly message - * Requirement 9.1: Display user-friendly error messages for network errors - * Requirement 9.2: Display specific error reason for prompt submission failures - * Requirement 9.3: Display connection troubleshooting guidance - * Requirement 9.4: Log and display generic message for unexpected errors - */ -export function formatErrorMessage(error: AppError): string { - switch (error.type) { - case ErrorType.NETWORK_ERROR: - return 'Network error occurred. Please check your internet connection and try again.'; - - case ErrorType.PROMPT_SUBMISSION_ERROR: - return `Failed to submit prompt: ${error.message}`; - - case ErrorType.CONNECTION_ERROR: - return 'Connection failed. Please check your network settings and ensure the relay server is accessible.'; - - case ErrorType.VALIDATION_ERROR: - return error.message; - - case ErrorType.PARSING_ERROR: - return 'Unable to process server response. The data may be corrupted.'; - - case ErrorType.UNEXPECTED_ERROR: - return 'An unexpected error occurred. Please try again.'; - - default: - return 'An error occurred. Please try again.'; - } -} - -/** - * Get actionable next steps for an error - * Requirement 9.5: Provide actionable next steps where applicable - */ -export function getActionableSteps(error: AppError): string[] { - if (error.actionableSteps && error.actionableSteps.length > 0) { - return error.actionableSteps; - } - - switch (error.type) { - case ErrorType.NETWORK_ERROR: - return [ - 'Check your internet connection', - 'Try switching between WiFi and mobile data', - 'Restart the app', - ]; - - case ErrorType.CONNECTION_ERROR: - return [ - 'Verify the relay server is running', - 'Check your network settings', - 'Try reconnecting manually', - 'Contact support if the issue persists', - ]; - - case ErrorType.PROMPT_SUBMISSION_ERROR: - return [ - 'Review your prompt for any issues', - 'Try submitting again', - 'Check your connection status', - ]; - - case ErrorType.VALIDATION_ERROR: - return ['Review the validation message', 'Correct the input and try again']; - - case ErrorType.PARSING_ERROR: - return ['Request fresh data from the server', 'Restart the app if the issue persists']; - - case ErrorType.UNEXPECTED_ERROR: - return [ - 'Try the action again', - 'Restart the app if the issue persists', - 'Contact support if the problem continues', - ]; - - default: - return ['Try again', 'Restart the app if the issue persists']; - } -} - -/** - * Create an AppError from various error sources - */ -export function createAppError( - type: ErrorType, - message: string, - originalError?: Error | unknown, - actionableSteps?: string[] -): AppError { - return { - type, - message, - originalError, - actionableSteps, - timestamp: Date.now(), - }; -} - -/** - * Log an error with appropriate detail level - * Requirement 9.4: Log errors for debugging - */ -export function logError(error: AppError): void { - const logMessage = `[${new Date(error.timestamp).toISOString()}] ${error.type}: ${error.message}`; - - // Check if __DEV__ is defined (React Native environment) - const isDevelopment = - typeof __DEV__ !== 'undefined' ? __DEV__ : process.env.NODE_ENV !== 'production'; - - if (isDevelopment) { - // In development, log full error details - console.error(logMessage); - if (error.originalError) { - console.error('Original error:', error.originalError); - } - if (error.actionableSteps) { - console.log('Actionable steps:', error.actionableSteps); - } - } else { - // In production, log minimal information - console.error(logMessage); - } -} - -/** - * Discriminate error type from various error sources - */ -export function discriminateErrorType(error: unknown): ErrorType { - if (error instanceof Error) { - const message = error.message.toLowerCase(); - - if (message.includes('network') || message.includes('fetch') || message.includes('timeout')) { - return ErrorType.NETWORK_ERROR; - } - - if ( - message.includes('connection') || - message.includes('socket') || - message.includes('disconnect') - ) { - return ErrorType.CONNECTION_ERROR; - } - - if (message.includes('validation') || message.includes('invalid')) { - return ErrorType.VALIDATION_ERROR; - } - - if (message.includes('parse') || message.includes('json') || message.includes('syntax')) { - return ErrorType.PARSING_ERROR; - } - } - - return ErrorType.UNEXPECTED_ERROR; -} - -/** - * Create a user-friendly error display object - */ -export interface ErrorDisplay { - title: string; - message: string; - actionableSteps: string[]; - severity: 'error' | 'warning' | 'info'; -} - -/** - * Convert an AppError to an ErrorDisplay for UI rendering - */ -export function createErrorDisplay(error: AppError): ErrorDisplay { - const message = formatErrorMessage(error); - const actionableSteps = getActionableSteps(error); - - let title: string; - let severity: 'error' | 'warning' | 'info' = 'error'; - - switch (error.type) { - case ErrorType.NETWORK_ERROR: - title = 'Network Error'; - break; - case ErrorType.CONNECTION_ERROR: - title = 'Connection Failed'; - break; - case ErrorType.PROMPT_SUBMISSION_ERROR: - title = 'Submission Failed'; - break; - case ErrorType.VALIDATION_ERROR: - title = 'Validation Error'; - severity = 'warning'; - break; - case ErrorType.PARSING_ERROR: - title = 'Data Error'; - break; - case ErrorType.UNEXPECTED_ERROR: - title = 'Error'; - break; - default: - title = 'Error'; - } - - return { - title, - message, - actionableSteps, - severity, - }; -} - -/** - * Handle an error by logging it and creating a display object - */ -export function handleError( - type: ErrorType, - message: string, - originalError?: Error | unknown, - actionableSteps?: string[] -): ErrorDisplay { - const appError = createAppError(type, message, originalError, actionableSteps); - logError(appError); - return createErrorDisplay(appError); -} - -/** - * Handle an unknown error by discriminating its type and creating a display object - */ -export function handleUnknownError(error: unknown): ErrorDisplay { - const type = discriminateErrorType(error); - const message = error instanceof Error ? error.message : 'An unknown error occurred'; - const appError = createAppError(type, message, error); - logError(appError); - return createErrorDisplay(appError); -} diff --git a/packages/mobile-client/src/utils/index.ts b/packages/mobile-client/src/utils/index.ts deleted file mode 100644 index becf0c7..0000000 --- a/packages/mobile-client/src/utils/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -// Utility functions for the mobile client -// This file will contain helper functions and utilities - -export const generateId = (): string => { - return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`; -}; - -// Export message validation utilities -export { - isInjectPromptMessage, - isInjectPromptResponse, - isSyncFullContextMessage, - validateProtocolMessage, - discriminateMessageType, -} from './messageValidation'; - -// Export error handling utilities -export { - ErrorType, - type AppError, - type ErrorDisplay, - formatErrorMessage, - getActionableSteps, - createAppError, - logError, - discriminateErrorType, - createErrorDisplay, - handleError, - handleUnknownError, -} from './errorHandling'; - -// Export platform adaptation utilities -export { - type PlatformType, - getCurrentPlatform, - isIOS, - isAndroid, - isWeb, - getStatusBarStyle, - getActivityIndicatorSize, - triggerHapticFeedback, - registerBackButtonHandler, - getKeyboardBehavior, - getKeyboardVerticalOffset, - supportsSwipeBack, - hasHardwareBackButton, - getNavigationGestureConfig, -} from './platformAdaptations'; - -// Export responsive layout utilities -export { - BREAKPOINTS, - MAX_CONTENT_WIDTH, - MIN_TOUCH_TARGET_SIZE, - type ScreenSize, - getScreenDimensions, - getScreenSize, - getGridColumns, - getBentoGridConfig, - getContentWidth, - getTypographyScale, - scaleFont, - scaleSpacing, - ensureMinTouchTarget, - getResponsivePadding, - isLargeScreen, - isSmallScreen, - getPixelRatio, - dpToPixels, - pixelsToDp, -} from './responsiveLayout'; diff --git a/packages/mobile-client/src/utils/messageValidation.ts b/packages/mobile-client/src/utils/messageValidation.ts deleted file mode 100644 index 70655c4..0000000 --- a/packages/mobile-client/src/utils/messageValidation.ts +++ /dev/null @@ -1,148 +0,0 @@ -import type { - InjectPromptMessage, - InjectPromptResponse, - SyncFullContextMessage, - ProtocolMessage, -} from '@codelink/protocol'; - -/** - * Type guard to check if a message is an InjectPromptMessage - */ -export function isInjectPromptMessage(message: unknown): message is InjectPromptMessage { - if (!message || typeof message !== 'object') { - return false; - } - - const msg = message as Record; - - // Check all required fields explicitly - if (msg.type !== 'INJECT_PROMPT') return false; - if (typeof msg.id !== 'string') return false; - if (typeof msg.timestamp !== 'number') return false; - if (!msg.payload || typeof msg.payload !== 'object') return false; - const payload = msg.payload as Record; - if (typeof payload.prompt !== 'string') return false; - - return true; -} - -/** - * Type guard to check if a message is an InjectPromptResponse - */ -export function isInjectPromptResponse(message: unknown): message is InjectPromptResponse { - if (!message || typeof message !== 'object') { - return false; - } - - const msg = message as Record; - - // Check all required fields explicitly - if (msg.type !== 'INJECT_PROMPT_RESPONSE') return false; - if (typeof msg.id !== 'string') return false; - if (typeof msg.timestamp !== 'number') return false; - if (typeof msg.originalId !== 'string') return false; - if (!msg.payload || typeof msg.payload !== 'object') return false; - const payload = msg.payload as Record; - if (typeof payload.success !== 'boolean') return false; - - // Check optional fields if present - if (payload.error !== undefined && typeof payload.error !== 'string') { - return false; - } - if (payload.editorUsed !== undefined && typeof payload.editorUsed !== 'string') { - return false; - } - - return true; -} - -/** - * Type guard to check if a message is a SyncFullContextMessage - */ -export function isSyncFullContextMessage(message: unknown): message is SyncFullContextMessage { - if (!message || typeof message !== 'object') { - return false; - } - - const msg = message as Record; - - // Check all required fields explicitly - if (msg.type !== 'SYNC_FULL_CONTEXT') return false; - if (typeof msg.id !== 'string') return false; - if (typeof msg.timestamp !== 'number') return false; - if (!msg.payload || typeof msg.payload !== 'object') return false; - const payload = msg.payload as Record; - if (typeof payload.fileName !== 'string') return false; - if (typeof payload.originalFile !== 'string') return false; - if (typeof payload.modifiedFile !== 'string') return false; - if (typeof payload.isDirty !== 'boolean') return false; - if (typeof payload.timestamp !== 'number') return false; - - return true; -} - -/** - * Validates that a message conforms to the ProtocolMessage interface - */ -export function validateProtocolMessage(message: unknown): { - isValid: boolean; - error?: string; -} { - if (!message || typeof message !== 'object') { - return { isValid: false, error: 'Message must be an object' }; - } - - const msg = message as Record; - - // Check base Message fields - if (typeof msg.id !== 'string') { - return { isValid: false, error: 'Message must have a string id' }; - } - - if (typeof msg.timestamp !== 'number') { - return { isValid: false, error: 'Message must have a number timestamp' }; - } - - if (typeof msg.type !== 'string') { - return { isValid: false, error: 'Message must have a string type' }; - } - - // Check specific message types - if ( - isInjectPromptMessage(message) || - isInjectPromptResponse(message) || - isSyncFullContextMessage(message) - ) { - return { isValid: true }; - } - - return { - isValid: false, - error: `Unknown or invalid message type: ${msg.type}`, - }; -} - -/** - * Discriminates message type and returns the specific message type - */ -export function discriminateMessageType( - message: ProtocolMessage -): - | { type: 'INJECT_PROMPT'; message: InjectPromptMessage } - | { type: 'INJECT_PROMPT_RESPONSE'; message: InjectPromptResponse } - | { type: 'SYNC_FULL_CONTEXT'; message: SyncFullContextMessage } - | { type: 'UNKNOWN'; message: ProtocolMessage } { - if (isInjectPromptMessage(message)) { - return { type: 'INJECT_PROMPT', message }; - } - - if (isInjectPromptResponse(message)) { - return { type: 'INJECT_PROMPT_RESPONSE', message }; - } - - if (isSyncFullContextMessage(message)) { - return { type: 'SYNC_FULL_CONTEXT', message }; - } - - return { type: 'UNKNOWN', message }; -} diff --git a/packages/mobile-client/src/utils/platformAdaptations.ts b/packages/mobile-client/src/utils/platformAdaptations.ts deleted file mode 100644 index 1c5259b..0000000 --- a/packages/mobile-client/src/utils/platformAdaptations.ts +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Platform-specific adaptations for iOS and Android - * - * Requirements: - * - 26.1: Platform-specific status bar styling - * - 26.2: Platform-specific safe area insets - * - 26.3: Platform-specific keyboard behavior - * - 26.4: Platform-specific haptic feedback patterns - * - 26.5: Platform-specific navigation gestures - * - 26.6: iOS swipe-back gesture support - * - 26.7: Android hardware back button support - * - 26.8: iOS-style activity indicator - * - 26.9: Android Material Design activity indicator - */ - -import { Platform, BackHandler } from 'react-native'; -import * as Haptics from 'expo-haptics'; - -/** - * Platform type - */ -export type PlatformType = 'ios' | 'android' | 'web' | 'other'; - -/** - * Get current platform - */ -export const getCurrentPlatform = (): PlatformType => { - if (Platform.OS === 'ios') return 'ios'; - if (Platform.OS === 'android') return 'android'; - if (Platform.OS === 'web') return 'web'; - return 'other'; -}; - -/** - * Check if running on iOS - */ -export const isIOS = (): boolean => Platform.OS === 'ios'; - -/** - * Check if running on Android - */ -export const isAndroid = (): boolean => Platform.OS === 'android'; - -/** - * Check if running on web - */ -export const isWeb = (): boolean => Platform.OS === 'web'; - -/** - * Get platform-specific status bar style - * Requirement 26.1: Platform-specific status bar styling - */ -export const getStatusBarStyle = (isDark: boolean): 'light' | 'dark' | 'auto' => { - if (isIOS()) { - // iOS uses light content for dark backgrounds, dark content for light backgrounds - return isDark ? 'light' : 'dark'; - } - - if (isAndroid()) { - // Android also follows the same pattern - return isDark ? 'light' : 'dark'; - } - - return 'auto'; -}; - -/** - * Get platform-specific activity indicator size - * Requirements 26.8, 26.9: Platform-specific activity indicators - */ -export const getActivityIndicatorSize = (): 'small' | 'large' => { - // Both platforms use the same sizes, but rendering differs - return 'large'; -}; - -/** - * Haptic feedback patterns for different platforms - * Requirement 26.4: Platform-specific haptic feedback patterns - */ -export const triggerHapticFeedback = async ( - type: 'light' | 'medium' | 'heavy' | 'success' | 'error' | 'selection' -): Promise => { - try { - if (isIOS()) { - // iOS haptic patterns - switch (type) { - case 'light': - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - break; - case 'medium': - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); - break; - case 'heavy': - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy); - break; - case 'success': - await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); - break; - case 'error': - await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); - break; - case 'selection': - await Haptics.selectionAsync(); - break; - } - } else if (isAndroid()) { - // Android haptic patterns (more subtle) - switch (type) { - case 'light': - case 'selection': - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - break; - case 'medium': - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); - break; - case 'heavy': - case 'success': - case 'error': - await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy); - break; - } - } - } catch (error) { - // Gracefully handle haptic feedback errors - console.warn('Haptic feedback not available:', error); - } -}; - -/** - * Register Android hardware back button handler - * Requirement 26.7: Android hardware back button support - * - * @param handler Function to call when back button is pressed - * @returns Cleanup function to remove the handler - */ -export const registerBackButtonHandler = (handler: () => boolean): (() => void) => { - if (isAndroid()) { - const subscription = BackHandler.addEventListener('hardwareBackPress', handler); - return () => subscription.remove(); - } - - // No-op for other platforms - return () => {}; -}; - -/** - * Get platform-specific keyboard behavior settings - * Requirement 26.3: Platform-specific keyboard behavior - */ -export const getKeyboardBehavior = (): 'padding' | 'height' | 'position' | undefined => { - if (isIOS()) { - return 'padding'; - } - - if (isAndroid()) { - return 'height'; - } - - return undefined; -}; - -/** - * Get platform-specific keyboard vertical offset - * Requirement 26.3: Platform-specific keyboard behavior - */ -export const getKeyboardVerticalOffset = (hasHeader: boolean = true): number => { - if (isIOS()) { - // iOS needs offset for header height - return hasHeader ? 64 : 0; - } - - // Android doesn't need offset - return 0; -}; - -/** - * Check if platform supports swipe-back gesture - * Requirement 26.6: iOS swipe-back gesture support - */ -export const supportsSwipeBack = (): boolean => { - return isIOS(); -}; - -/** - * Check if platform has hardware back button - * Requirement 26.7: Android hardware back button support - */ -export const hasHardwareBackButton = (): boolean => { - return isAndroid(); -}; - -/** - * Get platform-specific navigation gesture config - * Requirement 26.5: Platform-specific navigation gestures - */ -export const getNavigationGestureConfig = () => { - if (isIOS()) { - return { - gestureEnabled: true, - gestureDirection: 'horizontal' as const, - }; - } - - if (isAndroid()) { - return { - gestureEnabled: false, // Android uses hardware back button - gestureDirection: 'horizontal' as const, - }; - } - - return { - gestureEnabled: false, - gestureDirection: 'horizontal' as const, - }; -}; diff --git a/packages/mobile-client/src/utils/responsiveLayout.ts b/packages/mobile-client/src/utils/responsiveLayout.ts deleted file mode 100644 index d332857..0000000 --- a/packages/mobile-client/src/utils/responsiveLayout.ts +++ /dev/null @@ -1,253 +0,0 @@ -/** - * Responsive Layout Utilities - * - * Provides utilities for responsive layouts that adapt to different - * screen sizes and orientations. - * - * Requirements: - * - 13.1: Support portrait orientation on all screens - * - 13.2: Support landscape orientation on all screens - * - 13.3: Use maximum content width of 1024px on large screens - * - 13.4: Center content horizontally on wide screens - * - 13.5: Use responsive grid layouts (1 column on small, 2-3 on medium/large) - * - 13.6: Adjust bento grid layout based on orientation - * - 13.7: Adjust typography scales based on screen size - * - 13.8: Maintain minimum touch target size of 44x44pt on all screen sizes - * - 13.9: Re-layout content smoothly on orientation change - */ - -import { Dimensions, PixelRatio } from 'react-native'; -import type { Orientation } from '../hooks/useOrientation'; - -/** - * Screen size breakpoints (in dp/pt) - */ -export const BREAKPOINTS = { - small: 0, // 0-599dp (phones in portrait) - medium: 600, // 600-839dp (large phones, small tablets) - large: 840, // 840-1023dp (tablets) - xlarge: 1024, // 1024dp+ (large tablets, desktops) -} as const; - -/** - * Maximum content width for large screens (in dp/pt) - * Requirement 13.3: Use maximum content width of 1024px on large screens - */ -export const MAX_CONTENT_WIDTH = 1024; - -/** - * Minimum touch target size (in dp/pt) - * Requirement 13.8: Maintain minimum touch target size of 44x44pt - */ -export const MIN_TOUCH_TARGET_SIZE = 44; - -/** - * Screen size category - */ -export type ScreenSize = 'small' | 'medium' | 'large' | 'xlarge'; - -/** - * Get current screen dimensions - */ -export const getScreenDimensions = () => { - return Dimensions.get('window'); -}; - -/** - * Get screen size category based on width - * - * @param width Screen width in dp/pt - * @returns Screen size category - */ -export const getScreenSize = (width: number): ScreenSize => { - if (width >= BREAKPOINTS.xlarge) return 'xlarge'; - if (width >= BREAKPOINTS.large) return 'large'; - if (width >= BREAKPOINTS.medium) return 'medium'; - return 'small'; -}; - -/** - * Get number of grid columns based on screen size - * Requirement 13.5: Use responsive grid layouts (1 column on small, 2-3 on medium/large) - * - * @param screenSize Screen size category - * @param orientation Current orientation - * @returns Number of columns for grid layout - */ -export const getGridColumns = (screenSize: ScreenSize, orientation: Orientation): number => { - if (screenSize === 'small') { - return orientation === 'portrait' ? 1 : 2; - } - - if (screenSize === 'medium') { - return orientation === 'portrait' ? 2 : 3; - } - - // large and xlarge - return orientation === 'portrait' ? 2 : 3; -}; - -/** - * Get bento grid configuration based on screen size and orientation - * Requirement 13.6: Adjust bento grid layout based on orientation - * - * @param screenSize Screen size category - * @param orientation Current orientation - * @returns Bento grid configuration - */ -export const getBentoGridConfig = (screenSize: ScreenSize, orientation: Orientation) => { - const isSmall = screenSize === 'small'; - const isPortrait = orientation === 'portrait'; - - return { - // Use single column on small screens in portrait - columns: isSmall && isPortrait ? 1 : 2, - - // Asymmetrical sizing for bento pattern - largeCardSpan: isSmall && isPortrait ? 1 : 2, - smallCardSpan: 1, - - // Gap between cards - gap: isSmall ? 12 : 16, - - // Padding around grid - padding: isSmall ? 16 : 24, - }; -}; - -/** - * Get content container width with max width constraint - * Requirement 13.3: Use maximum content width of 1024px on large screens - * Requirement 13.4: Center content horizontally on wide screens - * - * @param screenWidth Current screen width - * @returns Content width and whether it should be centered - */ -export const getContentWidth = (screenWidth: number) => { - const shouldConstrain = screenWidth > MAX_CONTENT_WIDTH; - - return { - width: shouldConstrain ? MAX_CONTENT_WIDTH : screenWidth, - shouldCenter: shouldConstrain, - marginHorizontal: shouldConstrain ? (screenWidth - MAX_CONTENT_WIDTH) / 2 : 0, - }; -}; - -/** - * Get typography scale multiplier based on screen size - * Requirement 13.7: Adjust typography scales based on screen size - * - * @param screenSize Screen size category - * @returns Scale multiplier for font sizes - */ -export const getTypographyScale = (screenSize: ScreenSize): number => { - switch (screenSize) { - case 'small': - return 0.9; // Slightly smaller on small screens - case 'medium': - return 1.0; // Base scale - case 'large': - return 1.05; // Slightly larger on large screens - case 'xlarge': - return 1.1; // Larger on extra large screens - } -}; - -/** - * Scale a font size based on screen size - * - * @param baseSize Base font size - * @param screenSize Screen size category - * @returns Scaled font size - */ -export const scaleFont = (baseSize: number, screenSize: ScreenSize): number => { - return Math.round(baseSize * getTypographyScale(screenSize)); -}; - -/** - * Get spacing value based on screen size - * - * @param baseSpacing Base spacing value - * @param screenSize Screen size category - * @returns Scaled spacing value - */ -export const scaleSpacing = (baseSpacing: number, screenSize: ScreenSize): number => { - const scale = screenSize === 'small' ? 0.875 : 1.0; - return Math.round(baseSpacing * scale); -}; - -/** - * Ensure touch target meets minimum size requirement - * Requirement 13.8: Maintain minimum touch target size of 44x44pt - * - * @param size Desired size - * @returns Size that meets minimum requirement - */ -export const ensureMinTouchTarget = (size: number): number => { - return Math.max(size, MIN_TOUCH_TARGET_SIZE); -}; - -/** - * Get responsive padding based on screen size - * - * @param screenSize Screen size category - * @returns Padding values for different screen sizes - */ -export const getResponsivePadding = (screenSize: ScreenSize) => { - switch (screenSize) { - case 'small': - return { horizontal: 16, vertical: 12 }; - case 'medium': - return { horizontal: 24, vertical: 16 }; - case 'large': - case 'xlarge': - return { horizontal: 32, vertical: 20 }; - } -}; - -/** - * Check if screen is considered large - * - * @param width Screen width - * @returns True if screen is large or xlarge - */ -export const isLargeScreen = (width: number): boolean => { - return getScreenSize(width) === 'large' || getScreenSize(width) === 'xlarge'; -}; - -/** - * Check if screen is considered small - * - * @param width Screen width - * @returns True if screen is small - */ -export const isSmallScreen = (width: number): boolean => { - return getScreenSize(width) === 'small'; -}; - -/** - * Get pixel ratio for the current device - */ -export const getPixelRatio = (): number => { - return PixelRatio.get(); -}; - -/** - * Convert dp/pt to pixels - * - * @param dp Value in dp/pt - * @returns Value in pixels - */ -export const dpToPixels = (dp: number): number => { - return PixelRatio.getPixelSizeForLayoutSize(dp); -}; - -/** - * Convert pixels to dp/pt - * - * @param pixels Value in pixels - * @returns Value in dp/pt - */ -export const pixelsToDp = (pixels: number): number => { - return pixels / PixelRatio.get(); -}; diff --git a/packages/mobile-client/tsconfig.json b/packages/mobile-client/tsconfig.json deleted file mode 100644 index a8bf544..0000000 --- a/packages/mobile-client/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "node", - "resolveJsonModule": true, - "noEmit": true, - "jsx": "react-native", - "lib": ["ESNext", "DOM"], - "target": "ESNext", - "allowJs": true, - "isolatedModules": true, - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"], - "exclude": ["node_modules"], - "extends": "expo/tsconfig.base" -} diff --git a/packages/mobile-client/webpack.config.js b/packages/mobile-client/webpack.config.js deleted file mode 100644 index 91874c6..0000000 --- a/packages/mobile-client/webpack.config.js +++ /dev/null @@ -1,20 +0,0 @@ -const createExpoWebpackConfigAsync = require('@expo/webpack-config'); - -module.exports = async function (env, argv) { - const config = await createExpoWebpackConfigAsync( - { - ...env, - // Disable HMR for web to avoid the error - mode: env.mode || 'development', - }, - argv - ); - - // Disable HMR - if (config.devServer) { - config.devServer.hot = false; - config.devServer.liveReload = false; - } - - return config; -}; diff --git a/packages/mobile-client/.gitignore b/packages/mobile/.gitignore similarity index 52% rename from packages/mobile-client/.gitignore rename to packages/mobile/.gitignore index b065ca8..d914c32 100644 --- a/packages/mobile-client/.gitignore +++ b/packages/mobile/.gitignore @@ -1,9 +1,16 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + # Expo .expo/ dist/ web-build/ +expo-env.d.ts # Native +.kotlin/ *.orig.* *.jks *.p8 @@ -14,7 +21,7 @@ web-build/ # Metro .metro-health-check* -# Debug +# debug npm-debug.* yarn-debug.* yarn-error.* @@ -23,21 +30,12 @@ yarn-error.* .DS_Store *.pem -# Local env files +# local env files .env*.local -.env -# Typescript +# typescript *.tsbuildinfo -# Dependencies -node_modules/ - -# Testing -coverage/ - -/src/*/*.test.* - -/src/*/*.example.* -/src/*/*.md -/src/*/*/*.md \ No newline at end of file +# generated native folders +/ios +/android diff --git a/packages/mobile/app.json b/packages/mobile/app.json new file mode 100644 index 0000000..9e41b91 --- /dev/null +++ b/packages/mobile/app.json @@ -0,0 +1,37 @@ +{ + "expo": { + "name": "CodeLink", + "slug": "codelink", + "version": "2.0.0", + "scheme": "codelink", + "orientation": "portrait", + "userInterfaceStyle": "dark", + "platforms": ["ios", "android", "web"], + "plugins": [ + "expo-router", + "expo-secure-store", + [ + "expo-camera", + { + "cameraPermission": "CodeLink needs camera access to scan QR codes for pairing with VS Code." + } + ] + ], + "ios": { + "bundleIdentifier": "io.codelink.app" + }, + "android": { + "package": "io.codelink.app", + "adaptiveIcon": { + "backgroundColor": "#0a0a0a" + }, + "permissions": ["android.permission.CAMERA", "android.permission.RECORD_AUDIO"] + }, + "extra": { + "router": {}, + "eas": { + "projectId": "fbcaaefe-5e2b-40fd-8e50-3c4f846a52fd" + } + } + } +} diff --git a/packages/mobile/app/(tabs)/_layout.tsx b/packages/mobile/app/(tabs)/_layout.tsx new file mode 100644 index 0000000..522e106 --- /dev/null +++ b/packages/mobile/app/(tabs)/_layout.tsx @@ -0,0 +1,19 @@ +import { Tabs } from 'expo-router'; + +export default function TabsLayout() { + return ( + + + + + + ); +} diff --git a/packages/mobile/app/(tabs)/index.tsx b/packages/mobile/app/(tabs)/index.tsx new file mode 100644 index 0000000..1c520d4 --- /dev/null +++ b/packages/mobile/app/(tabs)/index.tsx @@ -0,0 +1,51 @@ +import { View, Text, StyleSheet } from 'react-native'; +import { useDiffStore } from '../../src/store/useDiffStore'; +import { useSessionStore } from '../../src/store/useSessionStore'; +import { DiffViewer } from '../../src/components/DiffViewer'; + +export default function EditorTab() { + const file = useDiffStore((s) => s.file); + const status = useSessionStore((s) => s.status); + + if (status !== 'connected') { + return ( + + ⚑ + Not connected + Go to Settings β†’ Pair Device to connect to VS Code. + + ); + } + + if (!file) { + return ( + + πŸ“„ + No file open + Open a file in VS Code and it will appear here. + + ); + } + + return ( + + ); +} + +const s = StyleSheet.create({ + center: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 32, + backgroundColor: '#1e1e1e', + }, + icon: { fontSize: 40, marginBottom: 16 }, + title: { color: '#ccc', fontSize: 18, fontWeight: '700', marginBottom: 8 }, + body: { color: '#666', fontSize: 14, textAlign: 'center', lineHeight: 22 }, +}); diff --git a/packages/mobile/app/(tabs)/prompt.tsx b/packages/mobile/app/(tabs)/prompt.tsx new file mode 100644 index 0000000..daaa1fe --- /dev/null +++ b/packages/mobile/app/(tabs)/prompt.tsx @@ -0,0 +1,66 @@ +import { View, Text, FlatList, StyleSheet } from 'react-native'; +import { usePromptStore, PromptEntry } from '../../src/store/usePromptStore'; +import { useSessionStore } from '../../src/store/useSessionStore'; +import { PromptComposer } from '../../src/components/PromptComposer'; + +function HistoryItem({ item }: { item: PromptEntry }) { + const color = item.status === 'success' ? '#4ec94e' : item.status === 'error' ? '#f55' : '#888'; + return ( + + + + + {item.text} + + {item.editorUsed && via {item.editorUsed}} + {item.error && {item.error}} + + + ); +} + +export default function PromptTab() { + const prompts = usePromptStore((s) => s.prompts); + const status = useSessionStore((s) => s.status); + + return ( + + {status !== 'connected' && ( + + Not connected β€” prompts won't be sent + + )} + p.id} + renderItem={({ item }) => } + ListEmptyComponent={ + + No prompts sent yet + + } + contentContainerStyle={{ flexGrow: 1 }} + style={s.list} + /> + + + ); +} + +const s = StyleSheet.create({ + root: { flex: 1, backgroundColor: '#1e1e1e' }, + banner: { backgroundColor: '#3a2a00', padding: 10, alignItems: 'center' }, + bannerText: { color: '#e5c07b', fontSize: 13 }, + list: { flex: 1 }, + empty: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 32 }, + emptyText: { color: '#555', fontSize: 14 }, +}); + +const h = StyleSheet.create({ + row: { flexDirection: 'row', padding: 12, borderBottomWidth: 1, borderBottomColor: '#2a2a2a' }, + dot: { width: 8, height: 8, borderRadius: 4, marginTop: 6, marginRight: 10 }, + content: { flex: 1 }, + text: { color: '#ccc', fontSize: 14 }, + meta: { color: '#555', fontSize: 12, marginTop: 4 }, + err: { color: '#f55', fontSize: 12, marginTop: 4 }, +}); diff --git a/packages/mobile/app/(tabs)/settings.tsx b/packages/mobile/app/(tabs)/settings.tsx new file mode 100644 index 0000000..0bc64fe --- /dev/null +++ b/packages/mobile/app/(tabs)/settings.tsx @@ -0,0 +1,111 @@ +import { View, Text, TouchableOpacity, StyleSheet, Alert } from 'react-native'; +import { useRouter } from 'expo-router'; +import { useSessionStore } from '../../src/store/useSessionStore'; +import { useDiffStore } from '../../src/store/useDiffStore'; +import { usePromptStore } from '../../src/store/usePromptStore'; +import { wsManager } from '../../src/ws/WsManager'; +import { clearStoredSession } from '../../src/api/authClient'; +import { patchEngine } from '../../src/diff/PatchEngine'; + +export default function SettingsTab() { + const router = useRouter(); + const status = useSessionStore((s) => s.status); + const sessionId = useSessionStore((s) => s.sessionId); + + const disconnect = () => { + Alert.alert( + 'Disconnect', + 'This will end the current session. You will need to scan a new QR code to reconnect.', + [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Disconnect', + style: 'destructive', + onPress: async () => { + wsManager.disconnect(); + await clearStoredSession(); + patchEngine.clearAll(); + useDiffStore.getState().clear(); + usePromptStore.getState().clear(); + useSessionStore.getState().reset(); + router.replace('/pairing'); + }, + }, + ] + ); + }; + + const statusColor = + status === 'connected' + ? '#4ec94e' + : status === 'connecting' + ? '#e5c07b' + : status === 'revoked' + ? '#f55' + : '#666'; + + return ( + + + + + + {status.charAt(0).toUpperCase() + status.slice(1)} + + + {sessionId && ( + + {sessionId.slice(0, 20)}… + + )} + + + + router.push('/pairing')}> + Pair New Device + + + + {status === 'connected' && ( + + + Disconnect Session + + + )} + + ); +} + +function Row({ label, children }: { label: string; children: React.ReactNode }) { + return ( + + {label} + {children} + + ); +} + +const s = StyleSheet.create({ + root: { flex: 1, backgroundColor: '#1e1e1e', padding: 16, gap: 12 }, + card: { backgroundColor: '#252526', borderRadius: 12, overflow: 'hidden' }, + dot: { width: 8, height: 8, borderRadius: 4, marginRight: 6 }, + value: { fontSize: 14, fontWeight: '600' }, + mono: { color: '#888', fontSize: 13, fontFamily: 'monospace' }, + pairBtn: { padding: 16, alignItems: 'center' }, + pairBtnText: { color: '#0078d4', fontSize: 15, fontWeight: '600' }, + disconnectBtn: { padding: 16, alignItems: 'center' }, + disconnectText: { color: '#f55', fontSize: 15, fontWeight: '600' }, +}); + +const r = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'center', + padding: 14, + borderBottomWidth: 1, + borderBottomColor: '#333', + }, + label: { color: '#888', fontSize: 14, flex: 1 }, + right: { flexDirection: 'row', alignItems: 'center' }, +}); diff --git a/packages/mobile/app/_layout.tsx b/packages/mobile/app/_layout.tsx new file mode 100644 index 0000000..2f46ba4 --- /dev/null +++ b/packages/mobile/app/_layout.tsx @@ -0,0 +1,17 @@ +import { Stack } from 'expo-router'; +import { StatusBar } from 'expo-status-bar'; + +export default function RootLayout() { + return ( + <> + + + + + + + + ); +} diff --git a/packages/mobile/app/index.tsx b/packages/mobile/app/index.tsx new file mode 100644 index 0000000..75c4a42 --- /dev/null +++ b/packages/mobile/app/index.tsx @@ -0,0 +1,37 @@ +import { useEffect } from 'react'; +import { View, ActivityIndicator, StyleSheet } from 'react-native'; +import { useRouter } from 'expo-router'; +import { getStoredSession } from '../src/api/authClient'; +import { wsManager } from '../src/ws/WsManager'; +import { handleMessage } from '../src/ws/MessageDispatcher'; +import { useSessionStore } from '../src/store/useSessionStore'; + +export default function Index() { + const router = useRouter(); + + useEffect(() => { + wsManager.onMessage = handleMessage; + wsManager.onConnected = () => useSessionStore.getState().setConnected('restored'); + wsManager.onDisconnected = () => {}; + + getStoredSession().then((session) => { + if (session) { + useSessionStore.getState().setConnecting(); + wsManager.connect(session.relayWss, session.mobileToken); + router.replace('/(tabs)'); + } else { + router.replace('/pairing'); + } + }); + }, []); + + return ( + + + + ); +} + +const s = StyleSheet.create({ + root: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#1e1e1e' }, +}); diff --git a/packages/mobile/app/pairing.tsx b/packages/mobile/app/pairing.tsx new file mode 100644 index 0000000..b34a918 --- /dev/null +++ b/packages/mobile/app/pairing.tsx @@ -0,0 +1,153 @@ +import { useState } from 'react'; +import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'; +import { CameraView, useCameraPermissions } from 'expo-camera'; +import { useRouter } from 'expo-router'; +import { joinSession, getDeviceId } from '../src/api/authClient'; +import { wsManager } from '../src/ws/WsManager'; +import { handleMessage } from '../src/ws/MessageDispatcher'; +import { useSessionStore } from '../src/store/useSessionStore'; +import { AUTH_URL } from '../src/api/config'; + +export default function PairingScreen() { + const [permission, requestPermission] = useCameraPermissions(); + const [scanned, setScanned] = useState(false); + const [joining, setJoining] = useState(false); + const [errorMsg, setErrorMsg] = useState(null); + const router = useRouter(); + + const handleScan = async ({ data }: { data: string }) => { + if (scanned || joining) { + return; + } + setScanned(true); + setJoining(true); + setErrorMsg(null); + + try { + // Decode URL-safe base64 payload from VS Code QR + const padded = data + '='.repeat((4 - (data.length % 4)) % 4); + const decoded = JSON.parse(atob(padded.replace(/-/g, '+').replace(/_/g, '/'))) as { + sessionId: string; + challenge: string; + relayWss: string; + }; + + const deviceId = await getDeviceId(); + const session = await joinSession(AUTH_URL, decoded.sessionId, decoded.challenge, deviceId); + + wsManager.onMessage = handleMessage; + wsManager.onConnected = () => { + useSessionStore.getState().setConnected(session.sessionId); + router.replace('/(tabs)'); + }; + wsManager.onDisconnected = () => {}; + wsManager.connect(session.relayWss, session.mobileToken); + } catch (err) { + setErrorMsg(err instanceof Error ? err.message : 'Unknown error'); + setJoining(false); + setScanned(false); + } + }; + + // ── Permission states ──────────────────────────────────────────────────── + if (!permission) { + return ( + + + + ); + } + + if (!permission.granted) { + return ( + + Camera access is needed to scan the QR code from VS Code. + + Allow Camera + + + ); + } + + if (joining) { + return ( + + + Connecting… + + ); + } + + // ── Main scanner UI ────────────────────────────────────────────────────── + return ( + + + + {/* Overlay */} + + Scan QR code + + In VS Code, run the command{'\n'} + CodeLink: Pair Mobile Device + + + {errorMsg && ( + + {errorMsg} + + )} + + + ); +} + +const s = StyleSheet.create({ + root: { flex: 1, backgroundColor: '#000' }, + center: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 32, + backgroundColor: '#1e1e1e', + }, + overlay: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'center', + alignItems: 'center', + paddingHorizontal: 28, + }, + title: { color: '#fff', fontSize: 24, fontWeight: '700', textAlign: 'center' }, + subtitle: { + color: 'rgba(255,255,255,0.7)', + fontSize: 14, + textAlign: 'center', + marginTop: 8, + lineHeight: 22, + }, + command: { color: '#4fc3f7', fontFamily: 'monospace' }, + frame: { + width: 260, + height: 260, + marginTop: 28, + borderWidth: 2, + borderColor: '#0078d4', + borderRadius: 16, + shadowColor: '#0078d4', + shadowOpacity: 0.6, + shadowRadius: 16, + }, + errorBox: { + marginTop: 20, + backgroundColor: 'rgba(200,50,50,0.85)', + borderRadius: 8, + padding: 12, + }, + errorText: { color: '#fff', fontSize: 13, textAlign: 'center' }, + body: { color: '#aaa', fontSize: 15, textAlign: 'center', marginBottom: 20, lineHeight: 22 }, + btn: { backgroundColor: '#0078d4', borderRadius: 10, paddingHorizontal: 28, paddingVertical: 14 }, + btnText: { color: '#fff', fontWeight: '700', fontSize: 15 }, +}); diff --git a/packages/mobile/assets/android-icon-background.png b/packages/mobile/assets/android-icon-background.png new file mode 100644 index 0000000..5ffefc5 Binary files /dev/null and b/packages/mobile/assets/android-icon-background.png differ diff --git a/packages/mobile/assets/android-icon-foreground.png b/packages/mobile/assets/android-icon-foreground.png new file mode 100644 index 0000000..3a9e501 Binary files /dev/null and b/packages/mobile/assets/android-icon-foreground.png differ diff --git a/packages/mobile/assets/android-icon-monochrome.png b/packages/mobile/assets/android-icon-monochrome.png new file mode 100644 index 0000000..77484eb Binary files /dev/null and b/packages/mobile/assets/android-icon-monochrome.png differ diff --git a/packages/mobile/assets/favicon.png b/packages/mobile/assets/favicon.png new file mode 100644 index 0000000..408bd74 Binary files /dev/null and b/packages/mobile/assets/favicon.png differ diff --git a/packages/mobile/assets/icon.png b/packages/mobile/assets/icon.png new file mode 100644 index 0000000..7165a53 Binary files /dev/null and b/packages/mobile/assets/icon.png differ diff --git a/packages/mobile-client/assets/splash-icon.png b/packages/mobile/assets/splash-icon.png similarity index 100% rename from packages/mobile-client/assets/splash-icon.png rename to packages/mobile/assets/splash-icon.png diff --git a/packages/mobile/eas.json b/packages/mobile/eas.json new file mode 100644 index 0000000..15fea6f --- /dev/null +++ b/packages/mobile/eas.json @@ -0,0 +1,18 @@ +{ + "cli": { "version": ">= 12.0.0" }, + "build": { + "preview": { + "android": { + "buildType": "apk" + }, + "env": { + "AUTH_URL": "http://192.168.x.x:8081" + } + }, + "production": { + "android": { + "buildType": "app-bundle" + } + } + } +} diff --git a/packages/mobile/metro.config.js b/packages/mobile/metro.config.js new file mode 100644 index 0000000..86d3bdf --- /dev/null +++ b/packages/mobile/metro.config.js @@ -0,0 +1,18 @@ +const { getDefaultConfig } = require('expo/metro-config'); +const path = require('path'); + +const projectRoot = __dirname; +const monorepoRoot = path.resolve(projectRoot, '../..'); + +const config = getDefaultConfig(projectRoot); + +// Watch all files within the monorepo +config.watchFolders = [monorepoRoot]; + +// Resolve packages from mobile first, then fall back to monorepo root +config.resolver.nodeModulesPaths = [ + path.resolve(projectRoot, 'node_modules'), + path.resolve(monorepoRoot, 'node_modules'), +]; + +module.exports = config; \ No newline at end of file diff --git a/packages/mobile/package.json b/packages/mobile/package.json new file mode 100644 index 0000000..1b2b059 --- /dev/null +++ b/packages/mobile/package.json @@ -0,0 +1,35 @@ +{ + "name": "mobile", + "version": "1.0.0", + "main": "expo-router/entry", + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web" + }, + "dependencies": { + "@codelink/protocol": "^2.0.0", + "@expo/metro-runtime": "~55.0.11", + "@expo/ngrok": "^4.1.3", + "babel-preset-expo": "^55.0.21", + "diff-match-patch": "^1.0.5", + "eas-cli": "^18.13.1", + "expo": "~55.0.24", + "expo-camera": "~55.0.18", + "expo-router": "~55.0.14", + "expo-secure-store": "~55.0.14", + "expo-status-bar": "~55.0.6", + "react": "19.2.0", + "react-dom": "19.2.0", + "react-native": "0.83.6", + "react-native-web": "^0.21.0", + "zustand": "^5.0.13" + }, + "devDependencies": { + "@types/diff-match-patch": "^1.0.36", + "@types/react": "~19.2.2", + "typescript": "~5.9.2" + }, + "private": true +} diff --git a/packages/mobile/src/api/authClient.ts b/packages/mobile/src/api/authClient.ts new file mode 100644 index 0000000..82eecd8 --- /dev/null +++ b/packages/mobile/src/api/authClient.ts @@ -0,0 +1,96 @@ +import { Platform } from 'react-native'; +import * as SecureStore from 'expo-secure-store'; + +const storage = { + async getItemAsync(key: string): Promise { + if (Platform.OS === 'web') { + return localStorage.getItem(key); + } + return SecureStore.getItemAsync(key); + }, + async setItemAsync(key: string, value: string): Promise { + if (Platform.OS === 'web') { + localStorage.setItem(key, value); + return; + } + return SecureStore.setItemAsync(key, value); + }, + async deleteItemAsync(key: string): Promise { + if (Platform.OS === 'web') { + localStorage.removeItem(key); + return; + } + return SecureStore.deleteItemAsync(key); + }, +}; + +const TOKEN_KEY = 'codelink.mobileToken'; +const SESSION_KEY = 'codelink.sessionId'; +const RELAY_KEY = 'codelink.relayWss'; +const DEVICE_ID_KEY = 'codelink.deviceId'; + +export interface StoredSession { + mobileToken: string; + sessionId: string; + relayWss: string; +} + +export async function getDeviceId(): Promise { + const existing = await storage.getItemAsync(DEVICE_ID_KEY); + if (existing) { + return existing; + } + const id = 'mob_' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2); + await storage.setItemAsync(DEVICE_ID_KEY, id); + return id; +} + +export async function joinSession( + authUrl: string, + sessionId: string, + challenge: string, + mobileDeviceId: string +): Promise { + const resp = await fetch(`${authUrl}/v1/sessions/${sessionId}/join`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ challenge, mobileDeviceId }), + }); + + if (!resp.ok) { + const body = (await resp.json().catch(() => ({}))) as { error?: string }; + throw new Error(body.error ?? `Join failed: ${resp.status}`); + } + + const data = (await resp.json()) as { + mobileToken: string; + relayWss: string; + sessionId: string; + }; + + await storage.setItemAsync(TOKEN_KEY, data.mobileToken); + await storage.setItemAsync(SESSION_KEY, data.sessionId); + await storage.setItemAsync(RELAY_KEY, data.relayWss); + + return { mobileToken: data.mobileToken, sessionId: data.sessionId, relayWss: data.relayWss }; +} + +export async function getStoredSession(): Promise { + const [mobileToken, sessionId, relayWss] = await Promise.all([ + storage.getItemAsync(TOKEN_KEY), + storage.getItemAsync(SESSION_KEY), + storage.getItemAsync(RELAY_KEY), + ]); + if (!mobileToken || !sessionId || !relayWss) { + return null; + } + return { mobileToken, sessionId, relayWss }; +} + +export async function clearStoredSession(): Promise { + await Promise.all([ + storage.deleteItemAsync(TOKEN_KEY), + storage.deleteItemAsync(SESSION_KEY), + storage.deleteItemAsync(RELAY_KEY), + ]); +} diff --git a/packages/mobile/src/api/config.ts b/packages/mobile/src/api/config.ts new file mode 100644 index 0000000..3099be4 --- /dev/null +++ b/packages/mobile/src/api/config.ts @@ -0,0 +1,2 @@ +export const AUTH_URL = process.env.EXPO_PUBLIC_AUTH_URL ?? 'http://localhost:8081'; +export const RELAY_URL = process.env.EXPO_PUBLIC_RELAY_URL ?? 'ws://localhost:8082'; diff --git a/packages/mobile/src/components/DiffViewer.tsx b/packages/mobile/src/components/DiffViewer.tsx new file mode 100644 index 0000000..dbce167 --- /dev/null +++ b/packages/mobile/src/components/DiffViewer.tsx @@ -0,0 +1,74 @@ +import React, { useRef } from 'react'; +import { View, Text, ScrollView, StyleSheet } from 'react-native'; + +interface Props { + fileName: string; + content: string; + isDirty: boolean; + cursorLine: number; +} + +export function DiffViewer({ fileName, content, isDirty, cursorLine }: Props) { + const scrollRef = useRef(null); + const lines = content.split('\n'); + const LINE_H = 20; + + // Scroll to cursor when it changes + React.useEffect(() => { + const offset = Math.max(0, cursorLine - 8) * LINE_H; + scrollRef.current?.scrollTo({ y: offset, animated: true }); + }, [cursorLine]); + + return ( + + {/* Header bar */} + + + {fileName} + + {isDirty && } + + + {/* Code view */} + + + + {lines.map((line, i) => ( + + {i + 1} + {line || ' '} + + ))} + + + + + ); +} + +const s = StyleSheet.create({ + root: { flex: 1, backgroundColor: '#1e1e1e' }, + header: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 16, + paddingVertical: 10, + backgroundColor: '#252526', + borderBottomWidth: 1, + borderBottomColor: '#333', + }, + fileName: { color: '#ccc', fontSize: 13, fontFamily: 'monospace', flex: 1 }, + dirtyDot: { width: 8, height: 8, borderRadius: 4, backgroundColor: '#e5c07b', marginLeft: 8 }, + scroll: { flex: 1 }, + row: { flexDirection: 'row', height: 20, alignItems: 'center', paddingRight: 16 }, + activeLine: { backgroundColor: '#2c3a4a' }, + gutter: { + width: 44, + textAlign: 'right', + paddingRight: 12, + color: '#5a5a5a', + fontSize: 12, + fontFamily: 'monospace', + }, + code: { color: '#d4d4d4', fontSize: 12, fontFamily: 'monospace' }, +}); diff --git a/packages/mobile/src/components/PromptComposer.tsx b/packages/mobile/src/components/PromptComposer.tsx new file mode 100644 index 0000000..a9d2822 --- /dev/null +++ b/packages/mobile/src/components/PromptComposer.tsx @@ -0,0 +1,78 @@ +import React, { useState } from 'react'; +import { + TextInput, + TouchableOpacity, + Text, + StyleSheet, + KeyboardAvoidingView, + Platform, +} from 'react-native'; +import { wsManager } from '../ws/WsManager'; +import { usePromptStore } from '../store/usePromptStore'; + +export function PromptComposer() { + const [text, setText] = useState(''); + const [sending, setSending] = useState(false); + const addPrompt = usePromptStore((s) => s.addPrompt); + + const send = async () => { + const trimmed = text.trim(); + if (!trimmed || sending || !wsManager.isConnected()) { + return; + } + + setSending(true); + const id = Math.random().toString(36).slice(2); + wsManager.send('INJECT_PROMPT', { prompt: trimmed }); + addPrompt(id, trimmed); + setText(''); + setSending(false); + }; + + const canSend = text.trim().length > 0 && !sending && wsManager.isConnected(); + + return ( + + + + {sending ? 'Sending…' : 'Send'} + + + ); +} + +const s = StyleSheet.create({ + root: { padding: 16, gap: 12 }, + input: { + backgroundColor: '#2a2a2a', + color: '#d4d4d4', + borderRadius: 10, + padding: 14, + fontSize: 15, + minHeight: 110, + textAlignVertical: 'top', + borderWidth: 1, + borderColor: '#383838', + }, + btn: { + backgroundColor: '#0078d4', + borderRadius: 10, + paddingVertical: 14, + alignItems: 'center', + }, + btnDisabled: { opacity: 0.35 }, + btnText: { color: '#fff', fontWeight: '700', fontSize: 15 }, +}); diff --git a/packages/mobile/src/diff/PatchEngine.ts b/packages/mobile/src/diff/PatchEngine.ts new file mode 100644 index 0000000..d9768a6 --- /dev/null +++ b/packages/mobile/src/diff/PatchEngine.ts @@ -0,0 +1,54 @@ +import { diff_match_patch } from 'diff-match-patch'; + +interface Snapshot { + content: string; + seq: number; +} + +class PatchEngineClass { + private readonly dmp = new diff_match_patch(); + private snapshots = new Map(); + + applySnapshot(fileName: string, content: string, seq: number): void { + this.snapshots.set(fileName, { content, seq }); + } + + applyPatch( + fileName: string, + patchText: string, + fromSeq: number, + toSeq: number + ): { content: string; success: boolean } | { gap: true } { + const snap = this.snapshots.get(fileName); + if (!snap) { + return { gap: true }; + } + if (snap.seq !== fromSeq) { + return { gap: true }; + } + + const patches = this.dmp.patch_fromText(patchText); + const [newContent, results] = this.dmp.patch_apply(patches, snap.content); + + if (results.some((r) => !r)) { + return { content: snap.content, success: false }; + } + + this.snapshots.set(fileName, { content: newContent, seq: toSeq }); + return { content: newContent, success: true }; + } + + currentContent(fileName: string): string | null { + return this.snapshots.get(fileName)?.content ?? null; + } + + clear(fileName: string): void { + this.snapshots.delete(fileName); + } + clearAll(): void { + this.snapshots.clear(); + } +} + +// Singleton β€” shared across WsManager and MessageDispatcher. +export const patchEngine = new PatchEngineClass(); diff --git a/packages/mobile/src/store/useDiffStore.ts b/packages/mobile/src/store/useDiffStore.ts new file mode 100644 index 0000000..186494a --- /dev/null +++ b/packages/mobile/src/store/useDiffStore.ts @@ -0,0 +1,35 @@ +import { create } from 'zustand'; + +interface FileState { + fileName: string; + content: string; + isDirty: boolean; + seq: number; + cursorLine: number; + cursorCol: number; +} + +interface DiffStore { + file: FileState | null; + setFile: (fileName: string, content: string, isDirty: boolean, seq: number) => void; + setCursor: (line: number, col: number) => void; + clear: () => void; +} + +export const useDiffStore = create((set) => ({ + file: null, + setFile: (fileName, content, isDirty, seq) => + set((s) => ({ + file: { + fileName, + content, + isDirty, + seq, + cursorLine: s.file?.cursorLine ?? 0, + cursorCol: s.file?.cursorCol ?? 0, + }, + })), + setCursor: (line, col) => + set((s) => (s.file ? { file: { ...s.file, cursorLine: line, cursorCol: col } } : {})), + clear: () => set({ file: null }), +})); diff --git a/packages/mobile/src/store/usePromptStore.ts b/packages/mobile/src/store/usePromptStore.ts new file mode 100644 index 0000000..aebaaf6 --- /dev/null +++ b/packages/mobile/src/store/usePromptStore.ts @@ -0,0 +1,32 @@ +import { create } from 'zustand'; + +export interface PromptEntry { + id: string; + text: string; + sentAt: number; + status: 'pending' | 'success' | 'error'; + editorUsed?: string; + error?: string; +} + +interface PromptStore { + prompts: PromptEntry[]; + addPrompt: (id: string, text: string) => void; + resolvePrompt: (id: string, success: boolean, editorUsed?: string, error?: string) => void; + clear: () => void; +} + +export const usePromptStore = create((set) => ({ + prompts: [], + addPrompt: (id, text) => + set((s) => ({ + prompts: [...s.prompts, { id, text, sentAt: Date.now(), status: 'pending' }], + })), + resolvePrompt: (id, success, editorUsed, error) => + set((s) => ({ + prompts: s.prompts.map((p) => + p.id === id ? { ...p, status: success ? 'success' : 'error', editorUsed, error } : p + ), + })), + clear: () => set({ prompts: [] }), +})); diff --git a/packages/mobile/src/store/useSessionStore.ts b/packages/mobile/src/store/useSessionStore.ts new file mode 100644 index 0000000..878c65e --- /dev/null +++ b/packages/mobile/src/store/useSessionStore.ts @@ -0,0 +1,21 @@ +import { create } from 'zustand'; + +type Status = 'idle' | 'connecting' | 'connected' | 'revoked'; + +interface SessionStore { + status: Status; + sessionId: string | null; + setConnecting: () => void; + setConnected: (sessionId: string) => void; + setRevoked: () => void; + reset: () => void; +} + +export const useSessionStore = create((set) => ({ + status: 'idle', + sessionId: null, + setConnecting: () => set({ status: 'connecting' }), + setConnected: (sessionId) => set({ status: 'connected', sessionId }), + setRevoked: () => set({ status: 'revoked', sessionId: null }), + reset: () => set({ status: 'idle', sessionId: null }), +})); diff --git a/packages/mobile/src/ws/MessageDispatcher.ts b/packages/mobile/src/ws/MessageDispatcher.ts new file mode 100644 index 0000000..4bffcc6 --- /dev/null +++ b/packages/mobile/src/ws/MessageDispatcher.ts @@ -0,0 +1,88 @@ +import { patchEngine } from '../diff/PatchEngine'; +import { useDiffStore } from '../store/useDiffStore'; +import { useSessionStore } from '../store/useSessionStore'; +import { usePromptStore } from '../store/usePromptStore'; +import { clearStoredSession } from '../api/authClient'; +import { wsManager } from './WsManager'; +import { isFileSnapshotPayload, isFilePatchPayload } from '@codelink/protocol'; + +export function handleMessage(type: string, payload: unknown, id: string): void { + switch (type) { + case 'HANDSHAKE_ACK': { + useSessionStore.getState().setConnected(id); + break; + } + + case 'FILE_SNAPSHOT': { + if (!isFileSnapshotPayload(payload)) { + return; + } + const p = payload; + const content = p.encoding === 'utf8' ? p.content : decodeGzip(p.content); + + patchEngine.applySnapshot(p.fileName, content, p.seq); + useDiffStore.getState().setFile(p.fileName, content, p.isDirty, p.seq); + wsManager.send('PATCH_ACK', { fileName: p.fileName, seq: p.seq }); + break; + } + + case 'FILE_PATCH': { + if (!isFilePatchPayload(payload)) { + return; + } + const p = payload; + const result = patchEngine.applyPatch(p.fileName, p.patches, p.fromSeq, p.toSeq); + + if ('gap' in result) { + wsManager.send('SNAPSHOT_REQUEST', { fileName: p.fileName, reason: 'gap' }); + return; + } + if (!result.success) { + wsManager.send('SNAPSHOT_REQUEST', { fileName: p.fileName, reason: 'corruption' }); + return; + } + useDiffStore.getState().setFile(p.fileName, result.content, p.isDirty, p.toSeq); + wsManager.send('PATCH_ACK', { fileName: p.fileName, seq: p.toSeq }); + break; + } + + case 'EDITOR_FOCUS': { + const p = payload as { cursorLine: number; cursorCol: number }; + useDiffStore.getState().setCursor(p.cursorLine, p.cursorCol); + break; + } + + case 'PROMPT_RESPONSE': { + const p = payload as { + originalId: string; + success: boolean; + editorUsed?: string; + error?: string; + }; + usePromptStore.getState().resolvePrompt(p.originalId, p.success, p.editorUsed, p.error); + break; + } + + case 'SESSION_REVOKED': { + wsManager.disconnect(); + clearStoredSession(); + patchEngine.clearAll(); + useDiffStore.getState().clear(); + usePromptStore.getState().clear(); + useSessionStore.getState().setRevoked(); + break; + } + } +} + +// Minimal gzip+base64 decoder using React Native's atob + pako (add pako if needed) +// For MVP: files under 64KB are sent uncompressed, so this path is rarely hit. +function decodeGzip(b64: string): string { + try { + // Fallback: return base64 string if decompression isn't available. + // TODO: add `pako` package and implement full gzip decompression. + return atob(b64); + } catch { + return ''; + } +} diff --git a/packages/mobile/src/ws/WsManager.ts b/packages/mobile/src/ws/WsManager.ts new file mode 100644 index 0000000..68bb2c6 --- /dev/null +++ b/packages/mobile/src/ws/WsManager.ts @@ -0,0 +1,86 @@ +import { buildEnvelope, MessageType, PayloadFor } from '@codelink/protocol'; + +type MessageHandler = (type: string, payload: unknown, id: string) => void; +type ConnectionHandler = () => void; + +class WsManagerClass { + private ws: WebSocket | null = null; + private url: string | null = null; + private token: string | null = null; + private reconnectDelay = 1_000; + private readonly MAX = 30_000; + private reconnectTimer: ReturnType | null = null; + private intentionalClose = false; + + onMessage: MessageHandler = () => {}; + onConnected: ConnectionHandler = () => {}; + onDisconnected: ConnectionHandler = () => {}; + + connect(url: string, token: string): void { + this.url = url; + this.token = token; + this.intentionalClose = false; + this._open(); + } + + private _open(): void { + if (!this.url || !this.token) { + return; + } + + const wsUrl = `${this.url}?token=${encodeURIComponent(this.token)}`; + this.ws = new WebSocket(wsUrl); + + this.ws.onopen = () => { + this.reconnectDelay = 1_000; + this.onConnected(); + }; + + this.ws.onmessage = ({ data }: MessageEvent) => { + try { + const e = JSON.parse(data) as { type: string; payload: unknown; id: string }; + this.onMessage(e.type, e.payload, e.id ?? ''); + } catch { + /* malformed β€” ignore */ + } + }; + + this.ws.onclose = () => { + this.onDisconnected(); + if (!this.intentionalClose) { + this._scheduleReconnect(); + } + }; + + this.ws.onerror = () => { + this.ws?.close(); + }; + } + + send(type: T, payload: PayloadFor): void { + if (this.ws?.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify(buildEnvelope(type, payload))); + } + } + + disconnect(): void { + this.intentionalClose = true; + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + } + this.ws?.close(1000, 'user action'); + this.ws = null; + } + + isConnected(): boolean { + return this.ws?.readyState === WebSocket.OPEN; + } + + private _scheduleReconnect(): void { + const jitter = Math.random() * 500; + this.reconnectTimer = setTimeout(() => this._open(), this.reconnectDelay + jitter); + this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.MAX); + } +} + +export const wsManager = new WsManagerClass(); diff --git a/packages/mobile/tsconfig.json b/packages/mobile/tsconfig.json new file mode 100644 index 0000000..9e34896 --- /dev/null +++ b/packages/mobile/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true, + "paths": { "@/*": ["./src/*"] } + } +} diff --git a/packages/protocol/dist/index.d.ts b/packages/protocol/dist/index.d.ts new file mode 100644 index 0000000..db4a363 --- /dev/null +++ b/packages/protocol/dist/index.d.ts @@ -0,0 +1,97 @@ +export declare const PROTOCOL_VERSION: 1; +/** + * Every message in both directions uses this wrapper. + * + * - `v` β€” always 1; lets future clients detect incompatible protocol versions + * - `id` β€” UUID v4; used to correlate responses (e.g. PROMPT_RESPONSE.originalId) + * - `ts` β€” unix milliseconds at send time; for latency measurement + * - `seq` β€” sender's outbound counter; receiver detects gaps and requests re-sync + * - `ack` β€” echoes the `id` of the message being acknowledged + */ +export interface MessageEnvelope { + v: typeof PROTOCOL_VERSION; + id: string; + ts: number; + type: T; + seq?: number; + ack?: string; + payload: PayloadFor; +} +export type MessageType = 'HANDSHAKE' | 'HANDSHAKE_ACK' | 'PING' | 'PONG' | 'SESSION_REVOKED' | 'FILE_SNAPSHOT' | 'FILE_PATCH' | 'EDITOR_FOCUS' | 'PATCH_ACK' | 'SNAPSHOT_REQUEST' | 'INJECT_PROMPT' | 'PROMPT_RESPONSE'; +/** + * Maps each MessageType to its payload interface. + * Used to make MessageEnvelope strongly typed end-to-end. + */ +export type PayloadFor = T extends 'HANDSHAKE' ? HandshakePayload : T extends 'HANDSHAKE_ACK' ? HandshakeAckPayload : T extends 'PING' ? PingPayload : T extends 'PONG' ? PongPayload : T extends 'SESSION_REVOKED' ? SessionRevokedPayload : T extends 'FILE_SNAPSHOT' ? FileSnapshotPayload : T extends 'FILE_PATCH' ? FilePatchPayload : T extends 'EDITOR_FOCUS' ? EditorFocusPayload : T extends 'PATCH_ACK' ? PatchAckPayload : T extends 'SNAPSHOT_REQUEST' ? SnapshotRequestPayload : T extends 'INJECT_PROMPT' ? InjectPromptPayload : T extends 'PROMPT_RESPONSE' ? PromptResponsePayload : never; +export interface HandshakePayload { + role: 'host' | 'client'; + capabilities: Capability[]; + clientVersion: string; +} +export interface HandshakeAckPayload { + connectionId: string; + serverCapabilities: Capability[]; + negotiatedCapabilities: Capability[]; +} +export interface PingPayload { + source: 'host' | 'client'; +} +export interface PongPayload { + originalId: string; +} +export interface SessionRevokedPayload { + reason: 'host_requested' | 'token_expired' | 'server_shutdown'; +} +export interface FileSnapshotPayload { + fileName: string; + content: string; + encoding: 'utf8' | 'gzip+base64'; + seq: number; + isDirty: boolean; + gitHead: boolean; + timestamp: number; +} +export interface FilePatchPayload { + fileName: string; + patches: string; + fromSeq: number; + toSeq: number; + isDirty: boolean; + timestamp: number; +} +export interface EditorFocusPayload { + fileName: string | null; + cursorLine: number; + cursorCol: number; +} +export interface PatchAckPayload { + fileName: string; + seq: number; +} +export interface SnapshotRequestPayload { + fileName: string; + reason: 'gap' | 'corruption' | 'reconnect'; +} +export interface InjectPromptPayload { + prompt: string; + targetFile?: string; +} +export interface PromptResponsePayload { + originalId: string; + success: boolean; + editorUsed?: string; + error?: string; +} +export type Capability = 'diff:snapshot' | 'diff:patch' | 'diff:focus' | 'prompt:inject' | 'session:revoke'; +/** All capabilities supported by the current protocol version */ +export declare const ALL_CAPABILITIES: Capability[]; +export declare function isMessageEnvelope(value: unknown): value is MessageEnvelope; +export declare function isFileSnapshotPayload(p: unknown): p is FileSnapshotPayload; +export declare function isFilePatchPayload(p: unknown): p is FilePatchPayload; +export declare function isInjectPromptPayload(p: unknown): p is InjectPromptPayload; +export declare function isSnapshotRequestPayload(p: unknown): p is SnapshotRequestPayload; +export declare function buildEnvelope(type: T, payload: PayloadFor, opts?: { + ack?: string; +}): MessageEnvelope; +export declare function parseEnvelope(raw: string): MessageEnvelope | null; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/protocol/dist/index.d.ts.map b/packages/protocol/dist/index.d.ts.map new file mode 100644 index 0000000..dcc48cb --- /dev/null +++ b/packages/protocol/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,EAAG,CAAU,CAAC;AAI3C;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IAClE,CAAC,EAAS,OAAO,gBAAgB,CAAC;IAClC,EAAE,EAAQ,MAAM,CAAC;IACjB,EAAE,EAAQ,MAAM,CAAC;IACjB,IAAI,EAAM,CAAC,CAAC;IACZ,GAAG,CAAC,EAAM,MAAM,CAAC;IACjB,GAAG,CAAC,EAAM,MAAM,CAAC;IACjB,OAAO,EAAG,UAAU,CAAC,CAAC,CAAC,CAAC;CACzB;AAID,MAAM,MAAM,WAAW,GAEnB,WAAW,GACX,eAAe,GACf,MAAM,GACN,MAAM,GACN,iBAAiB,GAGjB,eAAe,GACf,YAAY,GACZ,cAAc,GAGd,WAAW,GACX,kBAAkB,GAGlB,eAAe,GACf,iBAAiB,CAAC;AAItB;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,WAAW,IAC1C,CAAC,SAAS,WAAW,GAAU,gBAAgB,GAC/C,CAAC,SAAS,eAAe,GAAM,mBAAmB,GAClD,CAAC,SAAS,MAAM,GAAe,WAAW,GAC1C,CAAC,SAAS,MAAM,GAAe,WAAW,GAC1C,CAAC,SAAS,iBAAiB,GAAI,qBAAqB,GACpD,CAAC,SAAS,eAAe,GAAM,mBAAmB,GAClD,CAAC,SAAS,YAAY,GAAS,gBAAgB,GAC/C,CAAC,SAAS,cAAc,GAAO,kBAAkB,GACjD,CAAC,SAAS,WAAW,GAAU,eAAe,GAC9C,CAAC,SAAS,kBAAkB,GAAG,sBAAsB,GACrD,CAAC,SAAS,eAAe,GAAM,mBAAmB,GAClD,CAAC,SAAS,iBAAiB,GAAI,qBAAqB,GACpD,KAAK,CAAC;AAIR,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAW,MAAM,GAAG,QAAQ,CAAC;IACjC,YAAY,EAAG,UAAU,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAc,MAAM,CAAC;IACjC,kBAAkB,EAAQ,UAAU,EAAE,CAAC;IACvC,sBAAsB,EAAI,UAAU,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,gBAAgB,GAAG,eAAe,GAAG,iBAAiB,CAAC;CAChE;AAID,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAG,MAAM,CAAC;IAClB,OAAO,EAAI,MAAM,CAAC;IAClB,QAAQ,EAAG,MAAM,GAAG,aAAa,CAAC;IAElC,GAAG,EAAQ,MAAM,CAAC;IAElB,OAAO,EAAI,OAAO,CAAC;IAEnB,OAAO,EAAI,OAAO,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,EAAG,MAAM,CAAC;IAEjB,OAAO,EAAG,MAAM,CAAC;IAEjB,KAAK,EAAK,MAAM,CAAC;IAEjB,OAAO,EAAG,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAI,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAG,MAAM,CAAC;CACpB;AAID,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAO,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAI,KAAK,GAAG,YAAY,GAAG,WAAW,CAAC;CAC9C;AAID,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAQ,MAAM,CAAC;IACrB,UAAU,CAAC,EAAG,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAG,MAAM,CAAC;IACpB,OAAO,EAAM,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAO,MAAM,CAAC;CACrB;AAID,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,gBAAgB,CAAC;AAErB,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,EAAE,UAAU,EAMxC,CAAC;AAIF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAU1E;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,mBAAmB,CAU1E;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,gBAAgB,CASpE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,mBAAmB,CAI1E;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,sBAAsB,CAQhF;AAMD,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EACjD,IAAI,EAAK,CAAC,EACV,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EACtB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,eAAe,CAAC,CAAC,CAAC,CAUpB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAOjE"} \ No newline at end of file diff --git a/packages/protocol/dist/index.js b/packages/protocol/dist/index.js new file mode 100644 index 0000000..32a20a7 --- /dev/null +++ b/packages/protocol/dist/index.js @@ -0,0 +1,101 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ALL_CAPABILITIES = exports.PROTOCOL_VERSION = void 0; +exports.isMessageEnvelope = isMessageEnvelope; +exports.isFileSnapshotPayload = isFileSnapshotPayload; +exports.isFilePatchPayload = isFilePatchPayload; +exports.isInjectPromptPayload = isInjectPromptPayload; +exports.isSnapshotRequestPayload = isSnapshotRequestPayload; +exports.buildEnvelope = buildEnvelope; +exports.parseEnvelope = parseEnvelope; +exports.PROTOCOL_VERSION = 1; +/** All capabilities supported by the current protocol version */ +exports.ALL_CAPABILITIES = [ + 'diff:snapshot', + 'diff:patch', + 'diff:focus', + 'prompt:inject', + 'session:revoke', +]; +// ── Type guards ─────────────────────────────────────────────────────────────── +function isMessageEnvelope(value) { + if (typeof value !== 'object' || value === null) { + return false; + } + const v = value; + return (v['v'] === exports.PROTOCOL_VERSION && + typeof v['id'] === 'string' && + typeof v['ts'] === 'number' && + typeof v['type'] === 'string' && + 'payload' in v); +} +function isFileSnapshotPayload(p) { + if (typeof p !== 'object' || p === null) { + return false; + } + const o = p; + return (typeof o['fileName'] === 'string' && + typeof o['content'] === 'string' && + (o['encoding'] === 'utf8' || o['encoding'] === 'gzip+base64') && + typeof o['seq'] === 'number' && + typeof o['isDirty'] === 'boolean'); +} +function isFilePatchPayload(p) { + if (typeof p !== 'object' || p === null) { + return false; + } + const o = p; + return (typeof o['fileName'] === 'string' && + typeof o['patches'] === 'string' && + typeof o['fromSeq'] === 'number' && + typeof o['toSeq'] === 'number'); +} +function isInjectPromptPayload(p) { + if (typeof p !== 'object' || p === null) { + return false; + } + const o = p; + return typeof o['prompt'] === 'string' && o['prompt'].length > 0; +} +function isSnapshotRequestPayload(p) { + if (typeof p !== 'object' || p === null) { + return false; + } + const o = p; + return (typeof o['fileName'] === 'string' && + o['fileName'].length > 0 && + (o['reason'] === 'gap' || o['reason'] === 'corruption' || o['reason'] === 'reconnect')); +} +// ── Envelope builder ────────────────────────────────────────────────────────── +let _seq = 0; +function buildEnvelope(type, payload, opts) { + return { + v: exports.PROTOCOL_VERSION, + id: generateId(), + ts: Date.now(), + seq: ++_seq, + type, + payload, + ...(opts?.ack ? { ack: opts.ack } : {}), + }; +} +function parseEnvelope(raw) { + try { + const parsed = JSON.parse(raw); + return isMessageEnvelope(parsed) ? parsed : null; + } + catch { + return null; + } +} +function generateId() { + const globalCrypto = globalThis.crypto; + if (globalCrypto && typeof globalCrypto.randomUUID === 'function') { + return globalCrypto.randomUUID(); + } + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + const r = (Math.random() * 16) | 0; + return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16); + }); +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/protocol/dist/index.js.map b/packages/protocol/dist/index.js.map new file mode 100644 index 0000000..c0932fb --- /dev/null +++ b/packages/protocol/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AA8KA,8CAUC;AAED,sDAUC;AAED,gDASC;AAED,sDAIC;AAED,4DAQC;AAMD,sCAcC;AAED,sCAOC;AA5PY,QAAA,gBAAgB,GAAG,CAAU,CAAC;AAmK3C,iEAAiE;AACpD,QAAA,gBAAgB,GAAiB;IAC5C,eAAe;IACf,YAAY;IACZ,YAAY;IACZ,eAAe;IACf,gBAAgB;CACjB,CAAC;AAEF,iFAAiF;AAEjF,SAAgB,iBAAiB,CAAC,KAAc;IAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAClE,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,CAAC,CAAC,GAAG,CAAC,KAAQ,wBAAgB;QAC9B,OAAO,CAAC,CAAC,IAAI,CAAC,KAAO,QAAQ;QAC7B,OAAO,CAAC,CAAC,IAAI,CAAC,KAAO,QAAQ;QAC7B,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ;QAC7B,SAAS,IAAI,CAAC,CACf,CAAC;AACJ,CAAC;AAED,SAAgB,qBAAqB,CAAC,CAAU;IAC9C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,QAAQ;QACjC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAM,QAAQ;QACjC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,aAAa,CAAC;QAC7D,OAAO,CAAC,CAAC,KAAK,CAAC,KAAU,QAAQ;QACjC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAM,SAAS,CACnC,CAAC;AACJ,CAAC;AAED,SAAgB,kBAAkB,CAAC,CAAU;IAC3C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,QAAQ;QACjC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAM,QAAQ;QACjC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAM,QAAQ;QACjC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAQ,QAAQ,CAClC,CAAC;AACJ,CAAC;AAED,SAAgB,qBAAqB,CAAC,CAAU;IAC9C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AACnE,CAAC;AAED,SAAgB,wBAAwB,CAAC,CAAU;IACjD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,QAAQ;QACjC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,YAAY,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,CACvF,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF,IAAI,IAAI,GAAG,CAAC,CAAC;AAEb,SAAgB,aAAa,CAC3B,IAAU,EACV,OAAsB,EACtB,IAAuB;IAEvB,OAAO;QACL,CAAC,EAAQ,wBAAgB;QACzB,EAAE,EAAO,UAAU,EAAE;QACrB,EAAE,EAAO,IAAI,CAAC,GAAG,EAAE;QACnB,GAAG,EAAM,EAAE,IAAI;QACf,IAAI;QACJ,OAAO;QACP,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxC,CAAC;AACJ,CAAC;AAED,SAAgB,aAAa,CAAC,GAAW;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,YAAY,GAAI,UAAoE,CAAC,MAAM,CAAC;IAClG,IAAI,YAAY,IAAI,OAAO,YAAY,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAClE,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;IACD,OAAO,sCAAsC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE;QACjE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC"} \ No newline at end of file diff --git a/packages/protocol/package.json b/packages/protocol/package.json index e54315a..304e987 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,16 +1,19 @@ { "name": "@codelink/protocol", - "version": "0.1.0", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "version": "2.0.0", + "private": true, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], "scripts": { "build": "tsc", "dev": "tsc --watch", - "test": "vitest run", - "test:watch": "vitest" + "test": "vitest run" }, "devDependencies": { "typescript": "^5.0.0", - "vitest": "^4.1.0" + "vitest": "^4.0.0" } } diff --git a/packages/protocol/src/index.test.ts b/packages/protocol/src/index.test.ts index b5ed797..d442aa6 100644 --- a/packages/protocol/src/index.test.ts +++ b/packages/protocol/src/index.test.ts @@ -1,150 +1,161 @@ import { describe, it, expect } from 'vitest'; import { - MessageType, - PingMessage, - PongMessage, - FileContextPayload, - SyncFullContextMessage, - InjectPromptMessage, - InjectPromptResponse, + PROTOCOL_VERSION, + isMessageEnvelope, + isFileSnapshotPayload, + isFilePatchPayload, + isInjectPromptPayload, + buildEnvelope, + parseEnvelope, + isSnapshotRequestPayload, + ALL_CAPABILITIES, } from './index'; -describe('Protocol Package - Smoke Tests', () => { - describe('MessageType Enum', () => { - it('should have all required message types', () => { - expect(MessageType.PING).toBe('PING'); - expect(MessageType.PONG).toBe('PONG'); - expect(MessageType.SYNC_FULL_CONTEXT).toBe('SYNC_FULL_CONTEXT'); - expect(MessageType.INJECT_PROMPT).toBe('INJECT_PROMPT'); - expect(MessageType.INJECT_PROMPT_RESPONSE).toBe('INJECT_PROMPT_RESPONSE'); - }); +// ── isMessageEnvelope ───────────────────────────────────────────────────────── + +describe('isMessageEnvelope', () => { + it('accepts a valid envelope', () => { + expect(isMessageEnvelope({ + v: PROTOCOL_VERSION, + id: 'abc-123', + ts: Date.now(), + type: 'FILE_SNAPSHOT', + payload: {}, + })).toBe(true); }); - describe('PingMessage', () => { - it('should create a valid ping message', () => { - const ping: PingMessage = { - id: 'test-id', - timestamp: Date.now(), - type: 'ping', - source: 'extension', - }; - - expect(ping.id).toBe('test-id'); - expect(ping.type).toBe('ping'); - expect(ping.source).toBe('extension'); - expect(typeof ping.timestamp).toBe('number'); - }); + it('rejects wrong protocol version', () => { + expect(isMessageEnvelope({ + v: 99, id: 'x', ts: 0, type: 'PING', payload: {}, + })).toBe(false); }); - describe('PongMessage', () => { - it('should create a valid pong message', () => { - const pong: PongMessage = { - id: 'pong-id', - timestamp: Date.now(), - type: 'pong', - originalId: 'ping-id', - }; - - expect(pong.id).toBe('pong-id'); - expect(pong.type).toBe('pong'); - expect(pong.originalId).toBe('ping-id'); - expect(typeof pong.timestamp).toBe('number'); - }); + it('rejects missing payload', () => { + expect(isMessageEnvelope({ + v: PROTOCOL_VERSION, id: 'x', ts: 0, type: 'PING', + })).toBe(false); }); - describe('FileContextPayload', () => { - it('should create a valid file context payload', () => { - const payload: FileContextPayload = { - fileName: 'src/test.ts', - originalFile: 'original content', - modifiedFile: 'modified content', - isDirty: true, - timestamp: Date.now(), - }; - - expect(payload.fileName).toBe('src/test.ts'); - expect(payload.originalFile).toBe('original content'); - expect(payload.modifiedFile).toBe('modified content'); - expect(payload.isDirty).toBe(true); - expect(typeof payload.timestamp).toBe('number'); - }); + it('rejects null', () => expect(isMessageEnvelope(null)).toBe(false)); + it('rejects string', () => expect(isMessageEnvelope('hello')).toBe(false)); +}); + +// ── isFileSnapshotPayload ───────────────────────────────────────────────────── + +describe('isFileSnapshotPayload', () => { + const valid = { + fileName: 'src/main.ts', + content: 'console.log("hello")', + encoding: 'utf8' as const, + seq: 1, + isDirty: false, + gitHead: true, + timestamp: Date.now(), + }; + + it('accepts a valid payload', () => expect(isFileSnapshotPayload(valid)).toBe(true)); + it('accepts gzip+base64 encoding', () => expect(isFileSnapshotPayload({ ...valid, encoding: 'gzip+base64' })).toBe(true)); + it('rejects invalid encoding', () => expect(isFileSnapshotPayload({ ...valid, encoding: 'ascii' })).toBe(false)); + it('rejects missing fileName', () => expect(isFileSnapshotPayload({ ...valid, fileName: undefined })).toBe(false)); + it('rejects non-number seq', () => expect(isFileSnapshotPayload({ ...valid, seq: '1' })).toBe(false)); +}); + +// ── isFilePatchPayload ──────────────────────────────────────────────────────── + +describe('isFilePatchPayload', () => { + const valid = { + fileName: 'src/main.ts', + patches: '@@ -1,4 +1,5 @@\n hello\n+world\n', + fromSeq: 1, + toSeq: 2, + isDirty: true, + timestamp: Date.now(), + }; + + it('accepts a valid payload', () => expect(isFilePatchPayload(valid)).toBe(true)); + it('rejects missing patches', () => expect(isFilePatchPayload({ ...valid, patches: undefined })).toBe(false)); + it('rejects string fromSeq', () => expect(isFilePatchPayload({ ...valid, fromSeq: '1' })).toBe(false)); + it('rejects null', () => expect(isFilePatchPayload(null)).toBe(false)); +}); + +// ── isInjectPromptPayload ───────────────────────────────────────────────────── + +describe('isInjectPromptPayload', () => { + it('accepts a valid payload', () => expect(isInjectPromptPayload({ prompt: 'Refactor this' })).toBe(true)); + it('accepts optional fields', () => expect(isInjectPromptPayload({ prompt: 'x', targetFile: 'src/a.ts' })).toBe(true)); + it('rejects empty prompt', () => expect(isInjectPromptPayload({ prompt: '' })).toBe(false)); + it('rejects missing prompt', () => expect(isInjectPromptPayload({})).toBe(false)); +}); + +// ── isSnapshotRequestPayload ───────────────────────────────────────────────────── +describe('isSnapshotRequestPayload', () => { + it('accepts valid payload', () => expect(isSnapshotRequestPayload({ fileName: 'a.ts', reason: 'gap' })).toBe(true)); + it('accepts all reason values', () => { + expect(isSnapshotRequestPayload({ fileName: 'a.ts', reason: 'corruption' })).toBe(true); + expect(isSnapshotRequestPayload({ fileName: 'a.ts', reason: 'reconnect' })).toBe(true); }); + it('rejects invalid reason', () => expect(isSnapshotRequestPayload({ fileName: 'a.ts', reason: 'unknown' })).toBe(false)); + it('rejects empty fileName', () => expect(isSnapshotRequestPayload({ fileName: '', reason: 'gap' })).toBe(false)); + it('rejects null', () => expect(isSnapshotRequestPayload(null)).toBe(false)); +}); - describe('SyncFullContextMessage', () => { - it('should create a valid sync full context message', () => { - const message: SyncFullContextMessage = { - id: 'sync-id', - timestamp: Date.now(), - type: 'SYNC_FULL_CONTEXT', - payload: { - fileName: 'src/test.ts', - originalFile: 'original', - modifiedFile: 'modified', - isDirty: false, - timestamp: Date.now(), - }, - }; - - expect(message.id).toBe('sync-id'); - expect(message.type).toBe('SYNC_FULL_CONTEXT'); - expect(message.payload.fileName).toBe('src/test.ts'); - expect(typeof message.timestamp).toBe('number'); - }); +// ── buildEnvelope ───────────────────────────────────────────────────────────── + +describe('buildEnvelope', () => { + it('produces a valid envelope', () => { + const env = buildEnvelope('PING', { source: 'host' }); + expect(env.v).toBe(PROTOCOL_VERSION); + expect(env.type).toBe('PING'); + expect(env.payload).toEqual({ source: 'host' }); + expect(typeof env.id).toBe('string'); + expect(typeof env.ts).toBe('number'); + expect(typeof env.seq).toBe('number'); + }); + + it('increments seq on each call', () => { + const a = buildEnvelope('PING', { source: 'host' }); + const b = buildEnvelope('PING', { source: 'client' }); + expect(b.seq).toBeGreaterThan(a.seq!); + }); + + it('includes ack when provided', () => { + const env = buildEnvelope('PONG', { originalId: 'abc' }, { ack: 'abc' }); + expect(env.ack).toBe('abc'); + }); + + it('omits ack when not provided', () => { + const env = buildEnvelope('PING', { source: 'host' }); + expect(env.ack).toBeUndefined(); + }); +}); + +// ── parseEnvelope ───────────────────────────────────────────────────────────── + +describe('parseEnvelope', () => { + it('parses a valid JSON envelope', () => { + const env = buildEnvelope('EDITOR_FOCUS', { fileName: 'a.ts', cursorLine: 5, cursorCol: 3 }); + const json = JSON.stringify(env); + const parsed = parseEnvelope(json); + expect(parsed).not.toBeNull(); + expect(parsed?.type).toBe('EDITOR_FOCUS'); }); - describe('InjectPromptMessage', () => { - it('should create a valid inject prompt message', () => { - const message: InjectPromptMessage = { - id: 'prompt-id', - timestamp: Date.now(), - type: 'INJECT_PROMPT', - payload: { - prompt: 'Test prompt', - }, - }; - - expect(message.id).toBe('prompt-id'); - expect(message.type).toBe('INJECT_PROMPT'); - expect(message.payload.prompt).toBe('Test prompt'); - expect(typeof message.timestamp).toBe('number'); - }); + it('returns null for malformed JSON', () => { + expect(parseEnvelope('not json')).toBeNull(); }); - describe('InjectPromptResponse', () => { - it('should create a valid inject prompt response (success)', () => { - const response: InjectPromptResponse = { - id: 'response-id', - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: 'prompt-id', - payload: { - success: true, - editorUsed: 'continue', - }, - }; - - expect(response.id).toBe('response-id'); - expect(response.type).toBe('INJECT_PROMPT_RESPONSE'); - expect(response.originalId).toBe('prompt-id'); - expect(response.payload.success).toBe(true); - expect(response.payload.editorUsed).toBe('continue'); - }); - - it('should create a valid inject prompt response (error)', () => { - const response: InjectPromptResponse = { - id: 'response-id', - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: 'prompt-id', - payload: { - success: false, - error: 'No editor found', - }, - }; - - expect(response.id).toBe('response-id'); - expect(response.payload.success).toBe(false); - expect(response.payload.error).toBe('No editor found'); - }); + it('returns null for valid JSON but invalid envelope', () => { + expect(parseEnvelope(JSON.stringify({ foo: 'bar' }))).toBeNull(); }); }); + +// ── ALL_CAPABILITIES ────────────────────────────────────────────────────────── + +describe('ALL_CAPABILITIES', () => { + it('includes all expected capabilities', () => { + expect(ALL_CAPABILITIES).toContain('diff:snapshot'); + expect(ALL_CAPABILITIES).toContain('diff:patch'); + expect(ALL_CAPABILITIES).toContain('prompt:inject'); + expect(ALL_CAPABILITIES).toContain('session:revoke'); + }); +}); \ No newline at end of file diff --git a/packages/protocol/src/index.ts b/packages/protocol/src/index.ts index 4380b60..f23d8c7 100644 --- a/packages/protocol/src/index.ts +++ b/packages/protocol/src/index.ts @@ -1,205 +1,285 @@ -// Base message structure -export interface Message { +export const PROTOCOL_VERSION = 1 as const; + +// ── Envelope ───────────────────────────────────────────────────────────────── + +/** + * Every message in both directions uses this wrapper. + * + * - `v` β€” always 1; lets future clients detect incompatible protocol versions + * - `id` β€” UUID v4; used to correlate responses (e.g. PROMPT_RESPONSE.originalId) + * - `ts` β€” unix milliseconds at send time; for latency measurement + * - `seq` β€” sender's outbound counter; receiver detects gaps and requests re-sync + * - `ack` β€” echoes the `id` of the message being acknowledged + */ +export interface MessageEnvelope { + v: typeof PROTOCOL_VERSION; id: string; - timestamp: number; - type: string; + ts: number; + type: T; + seq?: number; + ack?: string; + payload: PayloadFor; } -// Message type enum -export enum MessageType { - PING = 'PING', - PONG = 'PONG', - SYNC_FULL_CONTEXT = 'SYNC_FULL_CONTEXT', - INJECT_PROMPT = 'INJECT_PROMPT', - INJECT_PROMPT_RESPONSE = 'INJECT_PROMPT_RESPONSE', +// ── Message types ───────────────────────────────────────────────────────────── + +export type MessageType = + // Session / control + | 'HANDSHAKE' // Cβ†’H optional capability negotiation after connect + | 'HANDSHAKE_ACK' // Rβ†’* relay confirms connection, echoes capabilities + | 'PING' // *β†’* application-level liveness check + | 'PONG' // *β†’* response to PING + | 'SESSION_REVOKED' // Rβ†’* host revoked the session; both sides should close + + // Diff sync + | 'FILE_SNAPSHOT' // Hβ†’C full file content; establishes the patch baseline + | 'FILE_PATCH' // Hβ†’C incremental diff-match-patch patch + | 'EDITOR_FOCUS' // Hβ†’C active file + cursor position changed + + // Acknowledgements + | 'PATCH_ACK' // Cβ†’H mobile applied a patch successfully + | 'SNAPSHOT_REQUEST' // Cβ†’H mobile detected a gap; requests a fresh snapshot + + // AI prompt injection + | 'INJECT_PROMPT' // Cβ†’H mobile sends a prompt to the active AI editor + | 'PROMPT_RESPONSE'; // Hβ†’C result of the prompt injection + +// ── Payload map ─────────────────────────────────────────────────────────────── + +/** + * Maps each MessageType to its payload interface. + * Used to make MessageEnvelope strongly typed end-to-end. + */ +export type PayloadFor = T extends 'HANDSHAKE' + ? HandshakePayload + : T extends 'HANDSHAKE_ACK' + ? HandshakeAckPayload + : T extends 'PING' + ? PingPayload + : T extends 'PONG' + ? PongPayload + : T extends 'SESSION_REVOKED' + ? SessionRevokedPayload + : T extends 'FILE_SNAPSHOT' + ? FileSnapshotPayload + : T extends 'FILE_PATCH' + ? FilePatchPayload + : T extends 'EDITOR_FOCUS' + ? EditorFocusPayload + : T extends 'PATCH_ACK' + ? PatchAckPayload + : T extends 'SNAPSHOT_REQUEST' + ? SnapshotRequestPayload + : T extends 'INJECT_PROMPT' + ? InjectPromptPayload + : T extends 'PROMPT_RESPONSE' + ? PromptResponsePayload + : never; + +// ── Payload interfaces ──────────────────────────────────────────────────────── + +export interface HandshakePayload { + role: 'host' | 'client'; + capabilities: Capability[]; + clientVersion: string; +} + +export interface HandshakeAckPayload { + connectionId: string; + serverCapabilities: Capability[]; + negotiatedCapabilities: Capability[]; } -// Example: Ping message from extension to relay -export interface PingMessage extends Message { - type: 'ping'; - source: 'extension' | 'mobile'; +export interface PingPayload { + source: 'host' | 'client'; } -// Example: Pong response from relay -export interface PongMessage extends Message { - type: 'pong'; +export interface PongPayload { originalId: string; } -// File context payload for diff viewing -export interface FileContextPayload { - fileName: string; // Workspace-relative file path (e.g., "src/index.ts") - originalFile: string; // Content from Git HEAD (empty string if untracked) - modifiedFile: string; // Current file content from disk - isDirty: boolean; // True if file has unsaved changes in editor - timestamp: number; // Unix timestamp in milliseconds when diff was generated +export interface SessionRevokedPayload { + reason: 'host_requested' | 'token_expired' | 'server_shutdown'; } -// Sync full context message for sending diffs to mobile -export interface SyncFullContextMessage extends Message { - type: 'SYNC_FULL_CONTEXT'; - payload: FileContextPayload; +// ── Diff sync payloads ─────────────────────────────────────────────────────── + +export interface FileSnapshotPayload { + fileName: string; + content: string; + encoding: 'utf8' | 'gzip+base64'; + + seq: number; + + isDirty: boolean; + + gitHead: boolean; + + timestamp: number; } -// Inject prompt message from mobile to relay -export interface InjectPromptMessage extends Message { - type: 'INJECT_PROMPT'; - payload: { - prompt: string; - }; +export interface FilePatchPayload { + fileName: string; + + patches: string; + + fromSeq: number; + + toSeq: number; + + isDirty: boolean; + timestamp: number; } -// Inject prompt response from relay to mobile -export interface InjectPromptResponse extends Message { - type: 'INJECT_PROMPT_RESPONSE'; - payload: { - success: boolean; - error?: string; - editorUsed?: string; - }; - originalId: string; +export interface EditorFocusPayload { + fileName: string | null; + cursorLine: number; + cursorCol: number; } -// Union type for all messages -export type ProtocolMessage = - | PingMessage - | PongMessage - | SyncFullContextMessage - | InjectPromptMessage - | InjectPromptResponse; +// ── Acknowledgement payloads ────────────────────────────────────────────────── -// Type guard functions for runtime type checking +export interface PatchAckPayload { + fileName: string; + seq: number; +} -/** - * Type guard to check if a value is a valid Message - */ -export function isMessage(value: unknown): value is Message { - return ( - typeof value === 'object' && - value !== null && - 'id' in value && - typeof (value as Message).id === 'string' && - 'timestamp' in value && - typeof (value as Message).timestamp === 'number' && - 'type' in value && - typeof (value as Message).type === 'string' - ); +export interface SnapshotRequestPayload { + fileName: string; + reason: 'gap' | 'corruption' | 'reconnect'; } -/** - * Type guard to check if a value is a PingMessage - */ -export function isPingMessage(value: unknown): value is PingMessage { - return ( - isMessage(value) && - (value as PingMessage).type === 'ping' && - 'source' in value && - ((value as PingMessage).source === 'extension' || (value as PingMessage).source === 'mobile') - ); +// ── Prompt payloads ─────────────────────────────────────────────────────────── + +export interface InjectPromptPayload { + prompt: string; + targetFile?: string; } -/** - * Type guard to check if a value is a PongMessage - */ -export function isPongMessage(value: unknown): value is PongMessage { - return ( - isMessage(value) && - (value as PongMessage).type === 'pong' && - 'originalId' in value && - typeof (value as PongMessage).originalId === 'string' - ); +export interface PromptResponsePayload { + originalId: string; + success: boolean; + editorUsed?: string; + error?: string; } -/** - * Type guard to check if a value is a valid FileContextPayload - */ -export function isFileContextPayload(value: unknown): value is FileContextPayload { +// ── Capabilities ────────────────────────────────────────────────────────────── + +export type Capability = + | 'diff:snapshot' // can send/receive FILE_SNAPSHOT + | 'diff:patch' // can send/receive FILE_PATCH + | 'diff:focus' // can send/receive EDITOR_FOCUS + | 'prompt:inject' // can send INJECT_PROMPT / receive PROMPT_RESPONSE + | 'session:revoke'; // understands SESSION_REVOKED + +/** All capabilities supported by the current protocol version */ +export const ALL_CAPABILITIES: Capability[] = [ + 'diff:snapshot', + 'diff:patch', + 'diff:focus', + 'prompt:inject', + 'session:revoke', +]; + +// ── Type guards ─────────────────────────────────────────────────────────────── + +export function isMessageEnvelope(value: unknown): value is MessageEnvelope { + if (typeof value !== 'object' || value === null) { + return false; + } + const v = value as Record; return ( - typeof value === 'object' && - value !== null && - 'fileName' in value && - typeof (value as FileContextPayload).fileName === 'string' && - 'originalFile' in value && - typeof (value as FileContextPayload).originalFile === 'string' && - 'modifiedFile' in value && - typeof (value as FileContextPayload).modifiedFile === 'string' && - 'isDirty' in value && - typeof (value as FileContextPayload).isDirty === 'boolean' && - 'timestamp' in value && - typeof (value as FileContextPayload).timestamp === 'number' + v['v'] === PROTOCOL_VERSION && + typeof v['id'] === 'string' && + typeof v['ts'] === 'number' && + typeof v['type'] === 'string' && + 'payload' in v ); } -/** - * Type guard to check if a value is a SyncFullContextMessage - */ -export function isSyncFullContextMessage(value: unknown): value is SyncFullContextMessage { +export function isFileSnapshotPayload(p: unknown): p is FileSnapshotPayload { + if (typeof p !== 'object' || p === null) { + return false; + } + const o = p as Record; return ( - isMessage(value) && - (value as SyncFullContextMessage).type === 'SYNC_FULL_CONTEXT' && - 'payload' in value && - isFileContextPayload((value as SyncFullContextMessage).payload) + typeof o['fileName'] === 'string' && + typeof o['content'] === 'string' && + (o['encoding'] === 'utf8' || o['encoding'] === 'gzip+base64') && + typeof o['seq'] === 'number' && + typeof o['isDirty'] === 'boolean' ); } -/** - * Type guard to check if a value is an InjectPromptMessage - */ -export function isInjectPromptMessage(value: unknown): value is InjectPromptMessage { +export function isFilePatchPayload(p: unknown): p is FilePatchPayload { + if (typeof p !== 'object' || p === null) { + return false; + } + const o = p as Record; return ( - isMessage(value) && - (value as InjectPromptMessage).type === 'INJECT_PROMPT' && - 'payload' in value && - typeof (value as InjectPromptMessage).payload === 'object' && - (value as InjectPromptMessage).payload !== null && - 'prompt' in (value as InjectPromptMessage).payload && - typeof (value as InjectPromptMessage).payload.prompt === 'string' + typeof o['fileName'] === 'string' && + typeof o['patches'] === 'string' && + typeof o['fromSeq'] === 'number' && + typeof o['toSeq'] === 'number' ); } -/** - * Type guard to check if a value is an InjectPromptResponse - */ -export function isInjectPromptResponse(value: unknown): value is InjectPromptResponse { - if (!isMessage(value) || (value as InjectPromptResponse).type !== 'INJECT_PROMPT_RESPONSE') { +export function isInjectPromptPayload(p: unknown): p is InjectPromptPayload { + if (typeof p !== 'object' || p === null) { return false; } + const o = p as Record; + return typeof o['prompt'] === 'string' && o['prompt'].length > 0; +} - const msg = value as InjectPromptResponse; - - if (!('originalId' in msg) || typeof msg.originalId !== 'string') { +export function isSnapshotRequestPayload(p: unknown): p is SnapshotRequestPayload { + if (typeof p !== 'object' || p === null) { return false; } + const o = p as Record; + return ( + typeof o['fileName'] === 'string' && + o['fileName'].length > 0 && + (o['reason'] === 'gap' || o['reason'] === 'corruption' || o['reason'] === 'reconnect') + ); +} - if (!('payload' in msg) || typeof msg.payload !== 'object' || msg.payload === null) { - return false; - } +// ── Envelope builder ────────────────────────────────────────────────────────── - if (!('success' in msg.payload) || typeof msg.payload.success !== 'boolean') { - return false; - } +let _seq = 0; - // Optional fields validation - if ('error' in msg.payload && typeof msg.payload.error !== 'string') { - return false; - } +export function buildEnvelope( + type: T, + payload: PayloadFor, + opts?: { ack?: string } +): MessageEnvelope { + return { + v: PROTOCOL_VERSION, + id: generateId(), + ts: Date.now(), + seq: ++_seq, + type, + payload, + ...(opts?.ack ? { ack: opts.ack } : {}), + }; +} - if ('editorUsed' in msg.payload && typeof msg.payload.editorUsed !== 'string') { - return false; +export function parseEnvelope(raw: string): MessageEnvelope | null { + try { + const parsed = JSON.parse(raw); + return isMessageEnvelope(parsed) ? parsed : null; + } catch { + return null; } - - return true; } -/** - * Type guard to check if a value is any valid ProtocolMessage - */ -export function isProtocolMessage(value: unknown): value is ProtocolMessage { - return ( - isPingMessage(value) || - isPongMessage(value) || - isSyncFullContextMessage(value) || - isInjectPromptMessage(value) || - isInjectPromptResponse(value) - ); +function generateId(): string { + const globalCrypto = (globalThis as unknown as { crypto?: { randomUUID?: () => string } }).crypto; + if (globalCrypto && typeof globalCrypto.randomUUID === 'function') { + return globalCrypto.randomUUID(); + } + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16); + }); } diff --git a/packages/protocol/tsconfig.json b/packages/protocol/tsconfig.json index 52d71c5..7a7c1e4 100644 --- a/packages/protocol/tsconfig.json +++ b/packages/protocol/tsconfig.json @@ -1,9 +1,18 @@ { - "extends": "../../tsconfig.base.json", "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], "outDir": "./dist", "rootDir": "./src", - "types": ["vitest/globals"] + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "composite": true }, - "include": ["src/**/*"] + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] } diff --git a/packages/protocol/tsconfig.tsbuildinfo b/packages/protocol/tsconfig.tsbuildinfo deleted file mode 100644 index 3f91a0a..0000000 --- a/packages/protocol/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@vitest/pretty-format/dist/index.d.ts","../../node_modules/@vitest/utils/dist/display.d.ts","../../node_modules/@vitest/utils/dist/types.d.ts","../../node_modules/@vitest/utils/dist/helpers.d.ts","../../node_modules/@vitest/utils/dist/timers.d.ts","../../node_modules/@vitest/utils/dist/index.d.ts","../../node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts","../../node_modules/@vitest/utils/dist/diff.d.ts","../../node_modules/@vitest/utils/diff.d.ts","../../node_modules/@vitest/runner/dist/tasks.d-Bh0IjN67.d.ts","../../node_modules/@vitest/runner/dist/index.d.ts","../../node_modules/vitest/dist/chunks/traces.d.402V_yFI.d.ts","../../node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts","../../node_modules/@vitest/snapshot/dist/rawSnapshot.d-D_X3-62x.d.ts","../../node_modules/@vitest/snapshot/dist/index.d.ts","../../node_modules/vitest/dist/chunks/config.d.ChUh6-ad.d.ts","../../node_modules/vitest/dist/chunks/environment.d.CrsxCzP1.d.ts","../../node_modules/vitest/dist/chunks/rpc.d.BFMWpdph.d.ts","../../node_modules/vitest/dist/chunks/worker.d.CckNUvI5.d.ts","../../node_modules/vitest/dist/chunks/browser.d.C0zGu1u9.d.ts","../../node_modules/@vitest/spy/optional-types.d.ts","../../node_modules/@vitest/spy/dist/index.d.ts","../../node_modules/tinyrainbow/dist/index.d.ts","../../node_modules/@standard-schema/spec/dist/index.d.ts","../../node_modules/@types/deep-eql/index.d.ts","../../node_modules/assertion-error/index.d.ts","../../node_modules/@types/chai/index.d.ts","../../node_modules/@vitest/expect/dist/index.d.ts","../../node_modules/@vitest/runner/dist/utils.d.ts","../../node_modules/@vitest/runner/utils.d.ts","../../node_modules/tinybench/dist/index.d.cts","../../node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts","../../node_modules/vitest/dist/chunks/global.d.D74z04P1.d.ts","../../node_modules/vitest/optional-runtime-types.d.ts","../../node_modules/@vitest/mocker/dist/types.d-BjI5eAwu.d.ts","../../node_modules/@vitest/mocker/dist/index.d-B41z0AuW.d.ts","../../node_modules/@vitest/mocker/dist/index.d.ts","../../node_modules/vitest/dist/chunks/suite.d.udJtyAgw.d.ts","../../node_modules/vitest/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts","../../node_modules/vitest/dist/runners.d.ts","../../node_modules/expect-type/dist/utils.d.ts","../../node_modules/expect-type/dist/overloads.d.ts","../../node_modules/expect-type/dist/branding.d.ts","../../node_modules/expect-type/dist/messages.d.ts","../../node_modules/expect-type/dist/index.d.ts","../../node_modules/vitest/dist/index.d.ts","./src/index.ts","./src/index.test.ts","../../node_modules/vitest/globals.d.ts"],"fileIdsList":[[70,71],[54,67,68,69,72],[80],[80,81],[51,54,55],[51,54],[74],[51],[46,51,58,59],[46,51,58],[66],[53],[46,52],[46],[48],[46,47,48,49,50],[86,87],[86,87,88,89],[86,88],[86],[56,75,76,78],[56,57,64,78],[46,54,56,60,78],[46,56,60,63,73,77,78],[56,57,60,78],[56,75,76,77,78],[56,61,62,63,78],[46,51,54,56,57,60,61,62,63,64,65,67,73,75,76,77,78,79,82,83,84,85,90],[46,54,56,57,60,61,75,76,77,78,83],[91],[91,92]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"3b0a56d056d81a011e484b9c05d5e430711aaecd561a788bad1d0498aad782c7","impliedFormat":99},{"version":"05c7aef6a4e496b93c2e682cced8903c0dfe6340d04f3fe616176e2782193435","impliedFormat":99},{"version":"9443967db823b66d1682be7fc66392be7c7924e10c3e54900f456341e94591a6","impliedFormat":99},{"version":"424f71d1fae96ac2e878af92345bb87bea1d29f757228fbc190133b305643f2c","impliedFormat":99},{"version":"ac3d263474022e9a14c43f588f485d549641d839b159ecc971978b90f34bdf6b","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"6bb6d57454370324434bcf355942dee45b0e0d8ab0fa3e98bafe8a30718273b4","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"a86701e56b10a6d1ef9b2ecaeedbab94ed7b957a646cd71fd09d02b323c6d3d7","impliedFormat":99},{"version":"976932e3807786cdae46ed5dfcd02c44f3fa25c157a0e8392f5a2dabb9a14a4e","impliedFormat":99},{"version":"59b7a8ec1781284f6602af48487b68fc3baadf34cb4cbcbb31f213b6712fac34","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"06db2f8ba1d1dfacf04529cb731081ab23f133f29c7608ebdfbcab356996827c","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"ed9bb55ddcbebd5cb3eee991f57ff21438546ee40ee1c310281bd12a6c7cf65b","impliedFormat":99},{"version":"e666e31d323fef5642f87db0da48a83e58f0aaf9e3823e87eabd8ec7e0441a36","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":1},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"4c3d12ac5744ff4ba2e1ce97ec307f09d726b4cfcfd5eff3315ccc080d620fb9","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"abdaf8c2f20089a6b23a6287007ed16f9cf76d0045ce2973a5f8508c87286d21","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"8c9917efcdf61e9b9a73ac1e289c612f12db33519ca1445cca41865f7887c737","impliedFormat":99},{"version":"c7fd5bd0fde25033dbcb83f31e5623aee2dba9162dc606d6399c179f965e4634","signature":"96628e84d5c62b05fe8ea0921d9a7cf4cbec939300742db6e754cb5aecfba537"},{"version":"5e142dc22df20a3bf149623b8ffa12597374b0b1daf755879868410017858e9e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8d7cbeea0454e05a3cdf3370c5df267072c4f1dc6c48a45a9ad750d7890443d7","affectsGlobalScope":true,"impliedFormat":99}],"root":[92,93],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":1,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":7},"referencedMap":[[72,1],[73,2],[81,3],[82,4],[56,5],[55,6],[74,5],[75,7],[58,8],[60,9],[59,10],[67,11],[54,12],[53,13],[47,14],[49,15],[51,16],[52,14],[88,17],[90,18],[89,19],[87,20],[77,21],[65,22],[61,23],[62,8],[78,24],[63,25],[83,26],[64,27],[91,28],[85,29],[94,30],[93,31]],"latestChangedDtsFile":"./dist/index.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/packages/relay-server/package.json b/packages/relay-server/package.json deleted file mode 100644 index 5a0e04e..0000000 --- a/packages/relay-server/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@codelink/relay-server", - "version": "0.1.0", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "start": "node dist/index.js" - }, - "dependencies": { - "@codelink/protocol": "*", - "socket.io": "^4.0.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.0.0" - } -} diff --git a/packages/relay-server/src/index.ts b/packages/relay-server/src/index.ts deleted file mode 100644 index 2817411..0000000 --- a/packages/relay-server/src/index.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { Server, Socket } from 'socket.io'; -import { - ProtocolMessage, - PingMessage, - PongMessage, - SyncFullContextMessage, - InjectPromptMessage, - InjectPromptResponse, -} from '@codelink/protocol'; - -// Track connected clients by type -export const extensionClients = new Set(); -export const mobileClients = new Set(); - -// Track pending prompt injection requests: messageId -> mobile socket -const pendingPromptRequests = new Map(); - -export function startServer(port: number = 8080): Server { - const io = new Server(port, { - cors: { - origin: '*', - }, - }); - - console.log(`[RelayServer] CodeLink Relay Server listening on port ${port}`); - - io.on('connection', (socket) => { - console.log(`[RelayServer] Client connected: ${socket.id}`); - - socket.on('message', (data: string) => { - try { - const message = parseMessage(data); - console.log(`[RelayServer] Received message type: ${message.type} from ${socket.id}`); - - if (message.type === 'ping') { - const pong = createPongMessage(message); - socket.emit('message', JSON.stringify(pong)); - console.log(`[RelayServer] Sent pong to ${socket.id}`); - - // Track client type based on ping source - if (message.source === 'extension') { - extensionClients.add(socket); - console.log( - `[RelayServer] Registered extension client: ${socket.id} (total: ${extensionClients.size})` - ); - } else if (message.source === 'mobile') { - mobileClients.add(socket); - console.log( - `[RelayServer] Registered mobile client: ${socket.id} (total: ${mobileClients.size})` - ); - } - } else if (message.type === 'SYNC_FULL_CONTEXT') { - console.log( - `[RelayServer] Routing SYNC_FULL_CONTEXT message to ${mobileClients.size} mobile clients` - ); - broadcastToMobileClients(message as SyncFullContextMessage); - } else if (message.type === 'INJECT_PROMPT') { - console.log( - `[RelayServer] Routing INJECT_PROMPT message to ${extensionClients.size} extension clients` - ); - // Track which mobile client sent this request - pendingPromptRequests.set(message.id, socket); - routeToExtensionClients(message as InjectPromptMessage, socket); - } else if (message.type === 'INJECT_PROMPT_RESPONSE') { - console.log(`[RelayServer] Routing INJECT_PROMPT_RESPONSE back to mobile client`); - const response = message as InjectPromptResponse; - - // Find the mobile client that sent the original request - if (response.originalId && pendingPromptRequests.has(response.originalId)) { - const mobileSocket = pendingPromptRequests.get(response.originalId)!; - pendingPromptRequests.delete(response.originalId); - - if (mobileSocket.connected) { - mobileSocket.emit('message', JSON.stringify(response)); - console.log(`[RelayServer] Routed response to mobile client ${mobileSocket.id}`); - } else { - console.log( - `[RelayServer] Mobile client ${mobileSocket.id} disconnected, cannot send response` - ); - } - } else { - // Fallback: broadcast to all mobile clients if we can't find the original requester - console.log( - `[RelayServer] No original request ID found, broadcasting to all mobile clients` - ); - broadcastResponseToMobileClients(response); - } - } - } catch (error) { - console.error(`[RelayServer] Error processing message from ${socket.id}:`, error); - } - }); - - socket.on('disconnect', () => { - console.log(`[RelayServer] Client disconnected: ${socket.id}`); - extensionClients.delete(socket); - mobileClients.delete(socket); - console.log( - `[RelayServer] Active clients - Extensions: ${extensionClients.size}, Mobile: ${mobileClients.size}` - ); - }); - - socket.on('error', (error) => { - console.error(`[RelayServer] Socket error for ${socket.id}:`, error); - }); - }); - - return io; -} - -export function broadcastToMobileClients(message: SyncFullContextMessage): void { - const messageStr = JSON.stringify(message); - let successCount = 0; - let errorCount = 0; - - mobileClients.forEach((client) => { - try { - if (client.connected) { - client.emit('message', messageStr); - successCount++; - } else { - // Remove disconnected clients - console.log(`[RelayServer] Removing disconnected client: ${client.id}`); - mobileClients.delete(client); - } - } catch (error) { - console.error(`[RelayServer] Error broadcasting to client ${client.id}:`, error); - errorCount++; - // Remove clients that error during broadcast - mobileClients.delete(client); - } - }); - - console.log( - `[RelayServer] Broadcast complete: ${successCount} successful, ${errorCount} errors, ${mobileClients.size} total mobile clients` - ); -} - -/** - * Broadcast INJECT_PROMPT_RESPONSE to all mobile clients (fallback when original requester is unknown) - */ -export function broadcastResponseToMobileClients(message: InjectPromptResponse): void { - const messageStr = JSON.stringify(message); - let successCount = 0; - - mobileClients.forEach((client) => { - try { - if (client.connected) { - client.emit('message', messageStr); - successCount++; - } - } catch (error) { - console.error(`[RelayServer] Error broadcasting response to client ${client.id}:`, error); - } - }); - - console.log(`[RelayServer] Broadcast response complete: ${successCount} mobile clients notified`); -} - -/** - * Route INJECT_PROMPT message to extension clients. - * The extension will handle the prompt injection and send back a response. - */ -export function routeToExtensionClients(message: InjectPromptMessage, originSocket: Socket): void { - if (extensionClients.size === 0) { - // No extension clients available - send error response back to mobile - const errorResponse: InjectPromptResponse = { - id: crypto.randomUUID(), - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: message.id, - payload: { - success: false, - error: 'No extension client is connected. Please ensure the VS Code extension is running.', - }, - }; - - originSocket.emit('message', JSON.stringify(errorResponse)); - console.log(`[RelayServer] No extension clients available for prompt injection`); - return; - } - - // Route to the first available extension client - // In the future, we could implement more sophisticated routing (e.g., round-robin, load balancing) - const messageStr = JSON.stringify(message); - let routed = false; - - for (const client of extensionClients) { - try { - if (client.connected) { - client.emit('message', messageStr); - routed = true; - console.log(`[RelayServer] Routed INJECT_PROMPT to extension client ${client.id}`); - break; // Only send to one extension - } else { - // Remove disconnected clients - console.log(`[RelayServer] Removing disconnected extension client: ${client.id}`); - extensionClients.delete(client); - } - } catch (error) { - console.error(`[RelayServer] Error routing to extension client ${client.id}:`, error); - extensionClients.delete(client); - } - } - - if (!routed) { - // All extension clients were disconnected - send error response - const errorResponse: InjectPromptResponse = { - id: crypto.randomUUID(), - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: message.id, - payload: { - success: false, - error: 'All extension clients are disconnected. Please check your VS Code extension.', - }, - }; - - originSocket.emit('message', JSON.stringify(errorResponse)); - console.log(`[RelayServer] Failed to route INJECT_PROMPT - all extension clients disconnected`); - } -} - -// Only start the server if this file is run directly (not imported) -if (require.main === module) { - const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 8080; - startServer(PORT); -} - -export function parseMessage(data: string): ProtocolMessage { - const parsed = JSON.parse(data); - - // Basic validation - if (!parsed.id || !parsed.timestamp || !parsed.type) { - throw new Error('Invalid message format: missing required fields'); - } - - return parsed as ProtocolMessage; -} - -export function createPongMessage(ping: PingMessage): PongMessage { - return { - id: crypto.randomUUID(), - timestamp: Date.now(), - type: 'pong', - originalId: ping.id, - }; -} diff --git a/packages/relay-server/tsconfig.json b/packages/relay-server/tsconfig.json deleted file mode 100644 index b6e4437..0000000 --- a/packages/relay-server/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "commonjs", - "lib": ["ES2020"], - "types": ["vitest/globals", "node"] - }, - "include": ["src/**/*"], - "references": [{ "path": "../protocol" }] -} diff --git a/packages/relay-server/tsconfig.tsbuildinfo b/packages/relay-server/tsconfig.tsbuildinfo deleted file mode 100644 index 46db634..0000000 --- a/packages/relay-server/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/engine.io-parser/build/esm/commons.d.ts","../../node_modules/engine.io-parser/build/esm/encodePacket.d.ts","../../node_modules/engine.io-parser/build/esm/decodePacket.d.ts","../../node_modules/engine.io-parser/build/esm/index.d.ts","../../node_modules/engine.io/build/parser-v3/index.d.ts","../../node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/@types/node/compatibility/index.d.ts","../../node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/@types/node/web-globals/events.d.ts","../../node_modules/buffer/index.d.ts","../../node_modules/undici-types/header.d.ts","../../node_modules/undici-types/readable.d.ts","../../node_modules/undici-types/file.d.ts","../../node_modules/undici-types/fetch.d.ts","../../node_modules/undici-types/formdata.d.ts","../../node_modules/undici-types/connector.d.ts","../../node_modules/undici-types/client.d.ts","../../node_modules/undici-types/errors.d.ts","../../node_modules/undici-types/dispatcher.d.ts","../../node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/undici-types/global-origin.d.ts","../../node_modules/undici-types/pool-stats.d.ts","../../node_modules/undici-types/pool.d.ts","../../node_modules/undici-types/handlers.d.ts","../../node_modules/undici-types/balanced-pool.d.ts","../../node_modules/undici-types/agent.d.ts","../../node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/undici-types/mock-agent.d.ts","../../node_modules/undici-types/mock-client.d.ts","../../node_modules/undici-types/mock-pool.d.ts","../../node_modules/undici-types/mock-errors.d.ts","../../node_modules/undici-types/proxy-agent.d.ts","../../node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/undici-types/retry-handler.d.ts","../../node_modules/undici-types/retry-agent.d.ts","../../node_modules/undici-types/api.d.ts","../../node_modules/undici-types/interceptors.d.ts","../../node_modules/undici-types/util.d.ts","../../node_modules/undici-types/cookies.d.ts","../../node_modules/undici-types/patch.d.ts","../../node_modules/undici-types/websocket.d.ts","../../node_modules/undici-types/eventsource.d.ts","../../node_modules/undici-types/filereader.d.ts","../../node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/undici-types/content-type.d.ts","../../node_modules/undici-types/cache.d.ts","../../node_modules/undici-types/index.d.ts","../../node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.generated.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/readline/promises.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/sea.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/test.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/@types/ws/index.d.ts","../../node_modules/engine.io/build/transport.d.ts","../../node_modules/engine.io/build/socket.d.ts","../../node_modules/@types/cors/index.d.ts","../../node_modules/engine.io/build/contrib/types.cookie.d.ts","../../node_modules/engine.io/build/server.d.ts","../../node_modules/engine.io/build/transports/polling.d.ts","../../node_modules/engine.io/build/transports/websocket.d.ts","../../node_modules/engine.io/build/transports/webtransport.d.ts","../../node_modules/engine.io/build/transports/index.d.ts","../../node_modules/engine.io/build/userver.d.ts","../../node_modules/engine.io/build/engine.io.d.ts","../../node_modules/@socket.io/component-emitter/lib/cjs/index.d.ts","../../node_modules/socket.io-parser/build/esm/index.d.ts","../../node_modules/socket.io/dist/typed-events.d.ts","../../node_modules/socket.io/dist/client.d.ts","../../node_modules/socket.io-adapter/dist/in-memory-adapter.d.ts","../../node_modules/socket.io-adapter/dist/cluster-adapter.d.ts","../../node_modules/socket.io-adapter/dist/index.d.ts","../../node_modules/socket.io/dist/socket-types.d.ts","../../node_modules/socket.io/dist/broadcast-operator.d.ts","../../node_modules/socket.io/dist/socket.d.ts","../../node_modules/socket.io/dist/namespace.d.ts","../../node_modules/socket.io/dist/index.d.ts","../protocol/dist/index.d.ts","./src/index.ts","../../node_modules/@vitest/pretty-format/dist/index.d.ts","../../node_modules/@vitest/utils/dist/display.d.ts","../../node_modules/@vitest/utils/dist/types.d.ts","../../node_modules/@vitest/utils/dist/helpers.d.ts","../../node_modules/@vitest/utils/dist/timers.d.ts","../../node_modules/@vitest/utils/dist/index.d.ts","../../node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts","../../node_modules/@vitest/utils/dist/diff.d.ts","../../node_modules/@vitest/utils/diff.d.ts","../../node_modules/@vitest/runner/dist/tasks.d-Bh0IjN67.d.ts","../../node_modules/@vitest/runner/dist/index.d.ts","../../node_modules/vitest/dist/chunks/traces.d.402V_yFI.d.ts","../../node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts","../../node_modules/@vitest/snapshot/dist/rawSnapshot.d-D_X3-62x.d.ts","../../node_modules/@vitest/snapshot/dist/index.d.ts","../../node_modules/vitest/dist/chunks/config.d.ChUh6-ad.d.ts","../../node_modules/vitest/dist/chunks/environment.d.CrsxCzP1.d.ts","../../node_modules/vitest/dist/chunks/rpc.d.BFMWpdph.d.ts","../../node_modules/vitest/dist/chunks/worker.d.CckNUvI5.d.ts","../../node_modules/vitest/dist/chunks/browser.d.C0zGu1u9.d.ts","../../node_modules/@vitest/spy/optional-types.d.ts","../../node_modules/@vitest/spy/dist/index.d.ts","../../node_modules/tinyrainbow/dist/index.d.ts","../../node_modules/@standard-schema/spec/dist/index.d.ts","../../node_modules/@types/deep-eql/index.d.ts","../../node_modules/assertion-error/index.d.ts","../../node_modules/@types/chai/index.d.ts","../../node_modules/@vitest/expect/dist/index.d.ts","../../node_modules/@vitest/runner/dist/utils.d.ts","../../node_modules/@vitest/runner/utils.d.ts","../../node_modules/tinybench/dist/index.d.cts","../../node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts","../../node_modules/vitest/dist/chunks/global.d.D74z04P1.d.ts","../../node_modules/vitest/optional-runtime-types.d.ts","../../node_modules/@vitest/mocker/dist/types.d-BjI5eAwu.d.ts","../../node_modules/@vitest/mocker/dist/index.d-B41z0AuW.d.ts","../../node_modules/@vitest/mocker/dist/index.d.ts","../../node_modules/vitest/dist/chunks/suite.d.udJtyAgw.d.ts","../../node_modules/vitest/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts","../../node_modules/vitest/dist/runners.d.ts","../../node_modules/expect-type/dist/utils.d.ts","../../node_modules/expect-type/dist/overloads.d.ts","../../node_modules/expect-type/dist/branding.d.ts","../../node_modules/expect-type/dist/messages.d.ts","../../node_modules/expect-type/dist/index.d.ts","../../node_modules/vitest/dist/index.d.ts","../../node_modules/vitest/globals.d.ts"],"fileIdsList":[[56,103],[56,103,202,203],[56,103,117,151],[56,100,103],[56,102,103],[103],[56,103,108,136],[56,103,104,109,114,122,133,144],[56,103,104,105,114,122],[51,52,53,56,103],[56,103,106,145],[56,103,107,108,115,123],[56,103,108,133,141],[56,103,109,111,114,122],[56,102,103,110],[56,103,111,112],[56,103,113,114],[56,102,103,114],[56,103,114,115,116,133,144],[56,103,114,115,116,129,133,136],[56,103,111,114,117,122,133,144],[56,103,114,115,117,118,122,133,141,144],[56,103,117,119,133,141,144],[54,55,56,57,58,59,60,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150],[56,103,114,120],[56,103,121,144,149],[56,103,111,114,122,133],[56,103,123],[56,103,124],[56,102,103,125],[56,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150],[56,103,127],[56,103,128],[56,103,114,129,130],[56,103,129,131,145,147],[56,103,114,133,134,136],[56,103,135,136],[56,103,133,134],[56,103,136],[56,103,137],[56,100,103,133,138],[56,103,114,139,140],[56,103,139,140],[56,103,108,122,133,141],[56,103,142],[56,103,122,143],[56,103,117,128,144],[56,103,108,145],[56,103,133,146],[56,103,121,147],[56,103,148],[56,98,103],[56,98,103,114,116,125,133,136,144,147,149],[56,103,133,150],[56,103,114,117,119,122,133,141,144,150,151],[56,103,186,199,200,201,204],[56,103,212],[56,103,212,213],[56,103,183,186,187],[56,103,183,186],[56,103,206],[56,103,183],[56,103,178,183,190,191],[56,103,178,183,190],[56,103,198],[56,103,185],[56,103,178,184],[56,103,178],[56,103,180],[56,103,178,179,180,181,182],[46,56,103],[46,47,48,56,103],[49,56,103,117,153,154,157,161,162],[56,103,114,117,133,152,153,154,155,156],[49,56,103,114,117,153,157],[49,50,56,103,114,117,152],[56,103,153,158,159,160],[49,56,103,153],[49,56,103,152,153],[56,103,153],[56,103,153,157],[56,103,218,219],[56,103,218,219,220,221],[56,103,218,220],[56,103,218],[56,103,168],[56,103,114],[56,103,168,169],[56,103,164],[56,103,166,170,171],[56,103,117,163,165,166,173,175],[56,103,117,118,119,163,165,166,170,171,172,173,174],[56,103,166,167,170,172,173,175],[56,103,117,128],[56,103,117,163,165,166,167,170,171,172,174],[56,70,74,103,144],[56,70,103,133,144],[56,65,103],[56,67,70,103,141,144],[56,103,122,141],[56,103,151],[56,65,103,151],[56,67,70,103,122,144],[56,62,63,66,69,103,114,133,144],[56,70,77,103],[56,62,68,103],[56,70,91,92,103],[56,66,70,103,136,144,151],[56,91,103,151],[56,64,65,103,151],[56,70,103],[56,64,65,66,67,68,69,70,71,72,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,103],[56,70,85,103],[56,70,77,78,103],[56,68,70,78,79,103],[56,69,103],[56,62,65,70,103],[56,70,74,78,79,103],[56,74,103],[56,68,70,73,103,144],[56,62,67,70,77,103],[56,103,133],[56,65,70,91,103,149,151],[56,103,188,207,208,210],[56,103,188,189,196,210],[56,103,178,186,188,192,210],[56,103,178,188,192,195,205,209,210],[56,103,188,189,192,210],[56,103,188,207,208,209,210],[56,103,188,193,194,195,210],[56,103,178,183,186,188,189,192,193,194,195,196,197,199,205,207,208,209,210,211,214,215,216,217,222],[56,103,178,186,188,189,192,193,207,208,209,210,215],[56,103,223],[56,103,175,176]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"569e762cf47aafdad508360a443c6c757e56c61db3b652b65458a7d168d139c4","impliedFormat":99},{"version":"02ed2766d79a00719ac3cc77851d54bd7197c1b12085ea12126bc2a65068223e","impliedFormat":99},{"version":"4b84373e192b7e0f8569b65eb16857098a6ee279b75d49223db2a751fdd7efde","impliedFormat":99},{"version":"5aeea312cd1d3cc5d72fc8a9c964439d771bdf41d9cce46667471b896b997473","impliedFormat":99},{"version":"cfa7bf135cafc5aad7cf544bc1cebf65a1fdb4373223cc85ea7d7196e18be151","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"641942a78f9063caa5d6b777c99304b7d1dc7328076038c6d94d8a0b81fc95c1","impliedFormat":1},{"version":"714435130b9015fae551788df2a88038471a5a11eb471f27c4ede86552842bc9","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e20d899c28ca26a2a7afc98beaa69e63ff7fba0a8bc47b4e3bf3ede5e09e424","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"372413016d17d804e1d139418aca0c68e47a83fb6669490857f4b318de8cccb3","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"085f552d005479e2e6a7311cdbbe5d8c55c497b4d19274285df161ee9684cd9c","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"007faacc9268357caa21d24169f3f3f2497af3e9241308df2d89f6e6d9bb3f2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"809821b8a065e3234a55b3a9d7846231ed18d66dd749f2494c66288d890daf7f","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7c5e2ea4a9749097c347454805e933844ed207b6eefec6b7cfd418b5f5f7b28","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"1ba59c8bbeed2cb75b239bb12041582fa3e8ef32f8d0bd0ec802e38442d3f317","impliedFormat":1},{"version":"f2c4a36eb216aadb0d9c79862a31b922ccfa1eaaa38d2124cc9192d40eda4779","impliedFormat":1},{"version":"cb5bb1db16ff4b534f56f7741e7ffd0a007ce36d387a377d4c196036e0932423","impliedFormat":1},{"version":"25be1eb939c9c63242c7a45446edb20c40541da967f43f1aa6a00ed53c0552db","impliedFormat":1},{"version":"08c2bb524b8ed271f194e1c7cc6ad0bcc773f596c41f68a207d0ec02c9727060","impliedFormat":1},{"version":"012b69bc8a16a21aa0863502339c49258c579723f9e7a54faa5f0d5c2b1ae1b7","impliedFormat":1},{"version":"29ad73d9e365d7b046f3168c6a510477bfe30d84a71cd7eb2f0e555b1d63f5f6","impliedFormat":1},{"version":"d99e9f5aa43397599fe824e38c33d13d3a9e19198806a4363114bd7ac58b29cc","impliedFormat":1},{"version":"440099416057789b14f85af057d4924915f27043399c10d4ca67409d94b963cf","impliedFormat":1},{"version":"ac44995fc7d0781d77927bae7dd41a31f0309e695fd2694b175c0ce3bc4b3b50","impliedFormat":1},{"version":"0c1f802f7a60ca8084e5188ac7952accdfc00f39ded3ebbbd3cdcc9da51b9a7b","impliedFormat":1},{"version":"a32e3fc530d8d1a18bf54678d8d55714827a50c9fabdd4ede7155a56be7d1dcb","impliedFormat":1},{"version":"14ecfc29e0c44ad4c5e50f9b597492cd8f45a2a635db8b5fe911a5da83e26cf8","impliedFormat":1},{"version":"ba0badf6398010ef0ba77947d4b52078fc84cb4f209f6b70194b3b2cfb1823d8","impliedFormat":99},{"version":"c2f041fe0e7ae2d5a19c477d19e8ec13de3d65ef45e442fa081cf6098cdcbe2d","impliedFormat":1},{"version":"0cef678147928ef223ff7f2aae3442cc9f4e9996956e9ac92434e626d0e147f8","impliedFormat":1},{"version":"198ae766bb698feb66d3188cfce59fb33696c951b10f901aa3fc3db4847ce76a","impliedFormat":1},{"version":"6dc488fd3d01e4269f0492b3e0ee7961eec79f4fc3ae997c7d28cde0572dbd91","impliedFormat":1},{"version":"a09b706f16bda9372761bd70cf59814b6f0a0c2970d62a5b2976e2fd157b920f","impliedFormat":1},{"version":"70da4bfde55d1ec74e3aa7635eae741f81ced44d3c344e2d299e677404570ca9","impliedFormat":1},{"version":"bf4f6b0d2ae8d11dc940c20891f9a4a558be906a530b9d9a8ff1032afa1962cd","impliedFormat":1},{"version":"9975431639f84750a914333bd3bfa9af47f86f54edbaa975617f196482cfee31","impliedFormat":1},{"version":"70a5cb56f988602271e772c65cb6735039148d5e90a4c270e5806f59fc51d3a0","impliedFormat":1},{"version":"e083384623f90cfa7e8d2aa7efe78c51095a04ad51d1f82c3e4052689666895d","impliedFormat":1},"96628e84d5c62b05fe8ea0921d9a7cf4cbec939300742db6e754cb5aecfba537",{"version":"47a8dce22d61c444444fda08ff4ce52cf058c548b9215263ea059f9c806dd983","signature":"eb5109c25a3aa6426353ac76dffb4e45ba153232262bf25c1ec63e2e3e7f5bec"},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"3b0a56d056d81a011e484b9c05d5e430711aaecd561a788bad1d0498aad782c7","impliedFormat":99},{"version":"05c7aef6a4e496b93c2e682cced8903c0dfe6340d04f3fe616176e2782193435","impliedFormat":99},{"version":"9443967db823b66d1682be7fc66392be7c7924e10c3e54900f456341e94591a6","impliedFormat":99},{"version":"424f71d1fae96ac2e878af92345bb87bea1d29f757228fbc190133b305643f2c","impliedFormat":99},{"version":"ac3d263474022e9a14c43f588f485d549641d839b159ecc971978b90f34bdf6b","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"6bb6d57454370324434bcf355942dee45b0e0d8ab0fa3e98bafe8a30718273b4","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"a86701e56b10a6d1ef9b2ecaeedbab94ed7b957a646cd71fd09d02b323c6d3d7","impliedFormat":99},{"version":"976932e3807786cdae46ed5dfcd02c44f3fa25c157a0e8392f5a2dabb9a14a4e","impliedFormat":99},{"version":"59b7a8ec1781284f6602af48487b68fc3baadf34cb4cbcbb31f213b6712fac34","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"06db2f8ba1d1dfacf04529cb731081ab23f133f29c7608ebdfbcab356996827c","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"ed9bb55ddcbebd5cb3eee991f57ff21438546ee40ee1c310281bd12a6c7cf65b","impliedFormat":99},{"version":"e666e31d323fef5642f87db0da48a83e58f0aaf9e3823e87eabd8ec7e0441a36","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":1},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"4c3d12ac5744ff4ba2e1ce97ec307f09d726b4cfcfd5eff3315ccc080d620fb9","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"abdaf8c2f20089a6b23a6287007ed16f9cf76d0045ce2973a5f8508c87286d21","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"8c9917efcdf61e9b9a73ac1e289c612f12db33519ca1445cca41865f7887c737","impliedFormat":99},{"version":"8d7cbeea0454e05a3cdf3370c5df267072c4f1dc6c48a45a9ad750d7890443d7","affectsGlobalScope":true,"impliedFormat":99}],"root":[177],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":1,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":7},"referencedMap":[[164,1],[201,1],[204,2],[155,3],[202,1],[100,4],[101,4],[102,5],[56,6],[103,7],[104,8],[105,9],[51,1],[54,10],[52,1],[53,1],[106,11],[107,12],[108,13],[109,14],[110,15],[111,16],[112,16],[113,17],[114,18],[115,19],[116,20],[57,1],[55,1],[117,21],[118,22],[119,23],[151,24],[120,25],[121,26],[122,27],[123,28],[124,29],[125,30],[126,31],[127,32],[128,33],[129,34],[130,34],[131,35],[132,1],[133,36],[135,37],[134,38],[136,39],[137,40],[138,41],[139,42],[140,43],[141,44],[142,45],[143,46],[144,47],[145,48],[146,49],[147,50],[148,51],[58,1],[59,1],[60,1],[99,52],[149,53],[150,54],[152,55],[205,56],[213,57],[214,58],[212,1],[178,1],[188,59],[187,60],[206,59],[207,61],[190,62],[192,63],[191,64],[199,65],[198,1],[186,66],[185,67],[179,68],[181,69],[183,70],[182,1],[184,68],[180,1],[203,1],[61,1],[46,1],[48,71],[47,71],[49,72],[156,1],[163,73],[50,1],[157,74],[154,75],[153,76],[161,77],[158,78],[159,79],[160,80],[162,81],[220,82],[222,83],[221,84],[219,85],[218,1],[169,86],[168,87],[170,88],[165,89],[172,90],[167,91],[175,92],[174,93],[171,94],[173,95],[166,87],[208,1],[200,1],[44,1],[45,1],[9,1],[8,1],[2,1],[10,1],[11,1],[12,1],[13,1],[14,1],[15,1],[16,1],[17,1],[3,1],[18,1],[19,1],[4,1],[20,1],[24,1],[21,1],[22,1],[23,1],[25,1],[26,1],[27,1],[5,1],[28,1],[29,1],[30,1],[31,1],[6,1],[35,1],[32,1],[33,1],[34,1],[36,1],[7,1],[37,1],[42,1],[43,1],[38,1],[39,1],[40,1],[41,1],[1,1],[77,96],[87,97],[76,96],[97,98],[68,99],[67,100],[96,101],[90,102],[95,103],[70,104],[84,105],[69,106],[93,107],[65,108],[64,101],[94,109],[66,110],[71,111],[72,1],[75,111],[62,1],[98,112],[88,113],[79,114],[80,115],[82,116],[78,117],[81,118],[91,101],[73,119],[74,120],[83,121],[63,122],[86,113],[85,111],[89,1],[92,123],[209,124],[197,125],[193,126],[194,62],[216,1],[210,127],[195,128],[215,129],[189,1],[196,130],[223,131],[217,132],[224,133],[211,1],[176,1],[177,134]],"latestChangedDtsFile":"./dist/index.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/KeyManager.d.ts b/packages/vscode-extension/dist/auth/KeyManager.d.ts new file mode 100644 index 0000000..e48e250 --- /dev/null +++ b/packages/vscode-extension/dist/auth/KeyManager.d.ts @@ -0,0 +1,12 @@ +import * as vscode from 'vscode'; +export declare class KeyManager { + private readonly secrets; + constructor(secrets: vscode.SecretStorage); + getOrCreateKeyPair(): Promise<{ + privateKeyPem: string; + publicKeyPem: string; + }>; + getPublicKeyPem(): Promise; + signRequest(body: string): Promise; +} +//# sourceMappingURL=KeyManager.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/KeyManager.d.ts.map b/packages/vscode-extension/dist/auth/KeyManager.d.ts.map new file mode 100644 index 0000000..1d7d085 --- /dev/null +++ b/packages/vscode-extension/dist/auth/KeyManager.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"KeyManager.d.ts","sourceRoot":"","sources":["../../src/auth/KeyManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAMjC,qBAAa,UAAU;IACP,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,MAAM,CAAC,aAAa;IAEpD,kBAAkB,IAAK,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAqB/E,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAKlC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAMnD"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/KeyManager.js b/packages/vscode-extension/dist/auth/KeyManager.js new file mode 100644 index 0000000..00f20bd --- /dev/null +++ b/packages/vscode-extension/dist/auth/KeyManager.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.KeyManager = void 0; +const crypto_1 = require("crypto"); +const PRIVATE_KEY_SECRET = 'codelink.rsa.privateKey'; +const PUBLIC_KEY_SECRET = 'codelink.rsa.publicKey'; +class KeyManager { + constructor(secrets) { + this.secrets = secrets; + } + async getOrCreateKeyPair() { + let privateKeyPem = await this.secrets.get(PRIVATE_KEY_SECRET); + let publicKeyPem = await this.secrets.get(PUBLIC_KEY_SECRET); + if (!privateKeyPem || !publicKeyPem) { + const { privateKey, publicKey } = (0, crypto_1.generateKeyPairSync)('rsa', { + modulusLength: 2048, + publicKeyEncoding: { type: 'spki', format: 'pem' }, + privateKeyEncoding: { type: 'pkcs8', format: 'pem' }, + }); + privateKeyPem = privateKey; + publicKeyPem = publicKey; + await this.secrets.store(PRIVATE_KEY_SECRET, privateKeyPem); + await this.secrets.store(PUBLIC_KEY_SECRET, publicKeyPem); + } + return { privateKeyPem, publicKeyPem }; + } + async getPublicKeyPem() { + const { publicKeyPem } = await this.getOrCreateKeyPair(); + return publicKeyPem; + } + async signRequest(body) { + const { privateKeyPem } = await this.getOrCreateKeyPair(); + const signer = (0, crypto_1.createSign)('SHA256'); + signer.update(body); + return signer.sign(privateKeyPem, 'base64'); + } +} +exports.KeyManager = KeyManager; +//# sourceMappingURL=KeyManager.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/KeyManager.js.map b/packages/vscode-extension/dist/auth/KeyManager.js.map new file mode 100644 index 0000000..f7e1df3 --- /dev/null +++ b/packages/vscode-extension/dist/auth/KeyManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"KeyManager.js","sourceRoot":"","sources":["../../src/auth/KeyManager.ts"],"names":[],"mappings":";;;AACA,mCAAyD;AAEzD,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;AACrD,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAEnD,MAAa,UAAU;IACnB,YAA6B,OAA6B;QAA7B,YAAO,GAAP,OAAO,CAAsB;IAAG,CAAC;IAE9D,KAAK,CAAC,kBAAkB;QACpB,IAAI,aAAa,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC/D,IAAI,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAE7D,IAAI,CAAC,aAAa,IAAI,CAAC,YAAY,EAAE,CAAC;YAClC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,IAAA,4BAAmB,EAAC,KAAK,EAAE;gBACzD,aAAa,EAAE,IAAI;gBACnB,iBAAiB,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;gBACjD,kBAAkB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE;aACvD,CAAC,CAAC;YAEH,aAAa,GAAG,UAAU,CAAC;YAC3B,YAAY,GAAG,SAAS,CAAC;YAEzB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;YAC5D,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,eAAe;QACjB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACzD,OAAO,YAAY,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAY;QAC1B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpB,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;CACJ;AAnCD,gCAmCC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/LaptopIdentity.d.ts b/packages/vscode-extension/dist/auth/LaptopIdentity.d.ts new file mode 100644 index 0000000..b2eaac5 --- /dev/null +++ b/packages/vscode-extension/dist/auth/LaptopIdentity.d.ts @@ -0,0 +1,12 @@ +import * as vscode from 'vscode'; +import { KeyManager } from './KeyManager'; +export declare class LaptopIdentity { + private readonly keyManager; + private readonly globalState; + constructor(keyManager: KeyManager, globalState: vscode.Memento); + ensureRegistered(): Promise; + private register; + getLaptopId(): string | undefined; + private getAuthUrl; +} +//# sourceMappingURL=LaptopIdentity.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/LaptopIdentity.d.ts.map b/packages/vscode-extension/dist/auth/LaptopIdentity.d.ts.map new file mode 100644 index 0000000..bbcb2f0 --- /dev/null +++ b/packages/vscode-extension/dist/auth/LaptopIdentity.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"LaptopIdentity.d.ts","sourceRoot":"","sources":["../../src/auth/LaptopIdentity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIzC,qBAAa,cAAc;IAEnB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBADX,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,MAAM,CAAC,OAAO;IAG1C,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;YAQ3B,QAAQ;IAmBtB,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC,OAAO,CAAC,UAAU;CAKrB"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/LaptopIdentity.js b/packages/vscode-extension/dist/auth/LaptopIdentity.js new file mode 100644 index 0000000..13da85b --- /dev/null +++ b/packages/vscode-extension/dist/auth/LaptopIdentity.js @@ -0,0 +1,76 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LaptopIdentity = void 0; +const vscode = __importStar(require("vscode")); +const LAPTOP_ID_KEY = 'codelink.laptopId'; +class LaptopIdentity { + constructor(keyManager, globalState) { + this.keyManager = keyManager; + this.globalState = globalState; + } + async ensureRegistered() { + const cached = this.globalState.get(LAPTOP_ID_KEY); + if (cached) { + return cached; + } + return this.register(); + } + async register() { + const authUrl = this.getAuthUrl(); + const publicKeyPem = await this.keyManager.getPublicKeyPem(); + const response = await fetch(`${authUrl}/v1/laptops/register`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ publicKeyPem }), + }); + if (!response.ok) { + throw new Error(`Laptop registration failed: ${response.status} ${response.statusText}`); + } + const { laptopId } = await response.json(); + await this.globalState.update(LAPTOP_ID_KEY, laptopId); + return laptopId; + } + getLaptopId() { + return this.globalState.get(LAPTOP_ID_KEY); + } + getAuthUrl() { + return vscode.workspace + .getConfiguration('codelink') + .get('authServiceUrl', 'http://localhost:8081'); + } +} +exports.LaptopIdentity = LaptopIdentity; +//# sourceMappingURL=LaptopIdentity.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/LaptopIdentity.js.map b/packages/vscode-extension/dist/auth/LaptopIdentity.js.map new file mode 100644 index 0000000..d1e13b6 --- /dev/null +++ b/packages/vscode-extension/dist/auth/LaptopIdentity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"LaptopIdentity.js","sourceRoot":"","sources":["../../src/auth/LaptopIdentity.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAgC;AAGhC,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAE1C,MAAa,cAAc;IACvB,YACqB,UAAsB,EACtB,WAA2B;QAD3B,eAAU,GAAV,UAAU,CAAY;QACtB,gBAAW,GAAX,WAAW,CAAgB;IAC7C,CAAC;IAEJ,KAAK,CAAC,gBAAgB;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAS,aAAa,CAAC,CAAC;QAC3D,IAAI,MAAM,EAAE,CAAC;YACT,OAAO,MAAM,CAAA;QACjB,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,QAAQ;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC;QAE7D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,sBAAsB,EAAE;YAC3D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;SACzC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QAC7F,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA0B,CAAC;QACnE,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QACvD,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,WAAW;QACP,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAS,aAAa,CAAC,CAAC;IACvD,CAAC;IAEO,UAAU;QACd,OAAO,MAAM,CAAC,SAAS;aAClB,gBAAgB,CAAC,UAAU,CAAC;aAC5B,GAAG,CAAS,gBAAgB,EAAE,uBAAuB,CAAC,CAAC;IAChE,CAAC;CACJ;AA1CD,wCA0CC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/SessionManager.d.ts b/packages/vscode-extension/dist/auth/SessionManager.d.ts new file mode 100644 index 0000000..30a05d6 --- /dev/null +++ b/packages/vscode-extension/dist/auth/SessionManager.d.ts @@ -0,0 +1,31 @@ +import * as vscode from 'vscode'; +import { KeyManager } from './KeyManager'; +export type SessionState = 'idle' | 'pending' | 'active' | 'revoked'; +export interface ActiveSession { + sessionId: string; + laptopToken: string; + relayWssUrl: string; +} +export declare class SessionManager { + private readonly keyManager; + private readonly laptopId; + private _state; + private _session; + private _pollTimer; + private readonly _onStateChange; + readonly onStateChange: vscode.Event; + constructor(keyManager: KeyManager, laptopId: string); + get state(): SessionState; + get session(): ActiveSession | null; + createSession(): Promise<{ + sessionId: string; + qrPayload: string; + expiresAt: number; + }>; + waitForMobile(sessionId: string, timeoutMs?: number): Promise; + revokeSession(): Promise; + dispose(): void; + private _setState; + private getAuthUrl; +} +//# sourceMappingURL=SessionManager.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/SessionManager.d.ts.map b/packages/vscode-extension/dist/auth/SessionManager.d.ts.map new file mode 100644 index 0000000..dbbe3ef --- /dev/null +++ b/packages/vscode-extension/dist/auth/SessionManager.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../src/auth/SessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,aAAa;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,cAAc;IASnB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAT7B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,UAAU,CAA+B;IAEjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;IAC1E,QAAQ,CAAC,aAAa,6BAA6B;gBAG9B,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM;IAGrC,IAAI,KAAK,IAAI,YAAY,CAA0B;IACnD,IAAI,OAAO,IAAI,aAAa,GAAG,IAAI,CAA0B;IAEvD,aAAa,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IA+BrF,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;IAmD5E,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBpC,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,UAAU;CAKrB"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/SessionManager.js b/packages/vscode-extension/dist/auth/SessionManager.js new file mode 100644 index 0000000..006622e --- /dev/null +++ b/packages/vscode-extension/dist/auth/SessionManager.js @@ -0,0 +1,148 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SessionManager = void 0; +const vscode = __importStar(require("vscode")); +class SessionManager { + constructor(keyManager, laptopId) { + this.keyManager = keyManager; + this.laptopId = laptopId; + this._state = 'idle'; + this._session = null; + this._pollTimer = null; + this._onStateChange = new vscode.EventEmitter(); + this.onStateChange = this._onStateChange.event; + } + get state() { return this._state; } + get session() { return this._session; } + async createSession() { + const authUrl = this.getAuthUrl(); + const requestedAt = Date.now(); + const body = JSON.stringify({ laptopId: this.laptopId, requestedAt }); + const sig = await this.keyManager.signRequest(body); + const response = await fetch(`${authUrl}/v1/sessions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Laptop-Id': this.laptopId, + 'X-Laptop-Sig': sig, + }, + body, + }); + if (!response.ok) { + throw new Error(`Session creation failed: ${response.status}`); + } + const data = await response.json(); + this._state = 'pending'; + this._onStateChange.fire('pending'); + return data; + } + async waitForMobile(sessionId, timeoutMs = 90000) { + const authUrl = this.getAuthUrl(); + const deadline = Date.now() + timeoutMs; + return new Promise((resolve, reject) => { + const poll = async () => { + if (Date.now() > deadline) { + this._setState('idle'); + return reject(new Error('Pairing timed out - QR code expired')); + } + try { + const sig = await this.keyManager.signRequest(''); + const response = await fetch(`${authUrl}/v1/sessions/${sessionId}/status`, { + headers: { + 'X-Laptop-Id': this.laptopId, + 'X-Laptop-Sig': sig, + }, + }); + if (!response.ok) { + return reject(new Error(`Status poll failed: ${response.status}`)); + } + const data = await response.json(); + if (data.state === 'active' && data.laptopToken) { + const relayBase = vscode.workspace + .getConfiguration('codelink') + .get('relayServiceUrl', 'ws://localhost:8082'); + const session = { + sessionId, + laptopToken: data.laptopToken, + relayWssUrl: `${relayBase}/ws` + }; + this._session = session; + this._setState('active'); + return resolve(session); + } + } + catch (err) { + reject(err); + } + }; + poll(); + }); + } + async revokeSession() { + if (!this._session) { + return; + } + const authUrl = this.getAuthUrl(); + const sig = await this.keyManager.signRequest(''); + await fetch(`${authUrl}/v1/sessions/${this._session.sessionId}`, { + method: 'DELETE', + headers: { + 'X-Laptop-Id': this.laptopId, + 'X-Laptop-Sig': sig, + }, + }); + this._session = null; + this._setState('revoked'); + setTimeout(() => this._setState('idle'), 1000); + } + dispose() { + if (this._pollTimer) { + clearTimeout(this._pollTimer); + } + this._onStateChange.dispose(); + } + _setState(state) { + this._state = state; + this._onStateChange.fire(state); + } + getAuthUrl() { + return vscode.workspace + .getConfiguration('codelink') + .get('authServiceUrl', 'http://localhost:8081'); + } +} +exports.SessionManager = SessionManager; +//# sourceMappingURL=SessionManager.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/auth/SessionManager.js.map b/packages/vscode-extension/dist/auth/SessionManager.js.map new file mode 100644 index 0000000..23b2576 --- /dev/null +++ b/packages/vscode-extension/dist/auth/SessionManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../../src/auth/SessionManager.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAgC;AAWhC,MAAa,cAAc;IAQvB,YACqB,UAAsB,EACtB,QAAgB;QADhB,eAAU,GAAV,UAAU,CAAY;QACtB,aAAQ,GAAR,QAAQ,CAAQ;QAT7B,WAAM,GAAiB,MAAM,CAAC;QAC9B,aAAQ,GAAyB,IAAI,CAAC;QACtC,eAAU,GAA0B,IAAI,CAAC;QAEhC,mBAAc,GAAG,IAAI,MAAM,CAAC,YAAY,EAAgB,CAAC;QACjE,kBAAa,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;IAKhD,CAAC;IAEJ,IAAI,KAAK,KAAqB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACnD,IAAI,OAAO,KAA2B,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE7D,KAAK,CAAC,aAAa;QACf,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;QACtE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAEpD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;YACnD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,IAAI,CAAC,QAAQ;gBAC5B,cAAc,EAAE,GAAG;aACtB;YACD,IAAI;SACP,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAI/B,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,SAAS,GAAG,KAAM;QACrD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QAExC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;gBACpB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;oBACxB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;oBACvB,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;gBACpE,CAAC;gBAED,IAAI,CAAC;oBACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;oBAClD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,gBAAgB,SAAS,SAAS,EAAE;wBACvE,OAAO,EAAE;4BACL,aAAa,EAAE,IAAI,CAAC,QAAQ;4BAC5B,cAAc,EAAE,GAAG;yBACtB;qBACJ,CAAC,CAAC;oBAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;wBACf,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;oBACvE,CAAC;oBAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAG/B,CAAC;oBAEF,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;wBAC9C,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS;6BAC7B,gBAAgB,CAAC,UAAU,CAAC;6BAC5B,GAAG,CAAS,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;wBAE3D,MAAM,OAAO,GAAkB;4BAC3B,SAAS;4BACT,WAAW,EAAE,IAAI,CAAC,WAAW;4BAC7B,WAAW,EAAE,GAAG,SAAS,KAAK;yBACjC,CAAC;wBACF,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;wBACxB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;wBACzB,OAAO,OAAO,CAAC,OAAO,CAAC,CAAA;oBAC3B,CAAC;gBACL,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACX,MAAM,CAAC,GAAG,CAAC,CAAC;gBAChB,CAAC;YACL,CAAC,CAAC;YACF,IAAI,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,aAAa;QACf,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAE/B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAElD,MAAM,KAAK,CAAC,GAAG,OAAO,gBAAgB,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE;YAC7D,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE;gBACL,aAAa,EAAE,IAAI,CAAC,QAAQ;gBAC5B,cAAc,EAAE,GAAG;aACtB;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC1B,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,IAAK,CAAC,CAAC;IACpD,CAAC;IAED,OAAO;QACH,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;IAEO,SAAS,CAAC,KAAmB;QACjC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAEO,UAAU;QACd,OAAO,MAAM,CAAC,SAAS;aAClB,gBAAgB,CAAC,UAAU,CAAC;aAC5B,GAAG,CAAS,gBAAgB,EAAE,uBAAuB,CAAC,CAAC;IAChE,CAAC;CACJ;AAtID,wCAsIC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/FileWatcher.d.ts b/packages/vscode-extension/dist/diff/FileWatcher.d.ts new file mode 100644 index 0000000..c8b31e6 --- /dev/null +++ b/packages/vscode-extension/dist/diff/FileWatcher.d.ts @@ -0,0 +1,23 @@ +import { GitIntegrationModule } from '../git/GitIntegrationModule'; +import { SnapshotEngine } from './SnapshotEngine'; +import { PatchEncoder } from './PatchEncoder'; +import { WsClient } from '../websocket/WsClient'; +export declare class FileWatcher { + private readonly git; + private readonly snapshot; + private readonly patches; + private readonly ws; + private disposables; + private debounceTimers; + private activeFile; + constructor(git: GitIntegrationModule, snapshot: SnapshotEngine, patches: PatchEncoder, ws: WsClient); + start(workspaceRoot: string): void; + stop(): void; + private onFileSwitch; + private scheduleSync; + private sendPatchOrSnapshot; + private sendSnapshot; + handleSnapshotRequest(fileName: string): Promise; + private getRelativeName; +} +//# sourceMappingURL=FileWatcher.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/FileWatcher.d.ts.map b/packages/vscode-extension/dist/diff/FileWatcher.d.ts.map new file mode 100644 index 0000000..ea8d0d9 --- /dev/null +++ b/packages/vscode-extension/dist/diff/FileWatcher.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"FileWatcher.d.ts","sourceRoot":"","sources":["../../src/diff/FileWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAIjD,qBAAa,WAAW;IAMhB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE;IARvB,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,UAAU,CAAuB;gBAGpB,GAAG,EAAE,oBAAoB,EACzB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,YAAY,EACrB,EAAE,EAAE,QAAQ;IAGjC,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;IA4BlC,IAAI,IAAI,IAAI;YAOE,YAAY;IAS1B,OAAO,CAAC,YAAY;YAoBN,mBAAmB;YAcnB,YAAY;IAyBpB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5D,OAAO,CAAC,eAAe;CAO1B"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/FileWatcher.js b/packages/vscode-extension/dist/diff/FileWatcher.js new file mode 100644 index 0000000..3203994 --- /dev/null +++ b/packages/vscode-extension/dist/diff/FileWatcher.js @@ -0,0 +1,158 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FileWatcher = void 0; +const vscode = __importStar(require("vscode")); +const path = __importStar(require("path")); +const DEBOUNCE_MS = 300; +class FileWatcher { + constructor(git, snapshot, patches, ws) { + this.git = git; + this.snapshot = snapshot; + this.patches = patches; + this.ws = ws; + this.disposables = []; + this.debounceTimers = new Map(); + this.activeFile = null; + } + start(workspaceRoot) { + this.git.initialize(workspaceRoot); + this.disposables.push(vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor?.document) { + this.onFileSwitch(editor.document); + } + })); + this.disposables.push(vscode.workspace.onDidSaveTextDocument(doc => { + this.scheduleSync(doc, false); + })); + this.disposables.push(vscode.workspace.onDidChangeTextDocument(event => { + this.scheduleSync(event.document, true); + })); + if (vscode.window.activeTextEditor?.document) { + this.onFileSwitch(vscode.window.activeTextEditor.document); + } + } + stop() { + this.debounceTimers.forEach(t => clearTimeout(t)); + this.debounceTimers.clear(); + this.disposables.forEach(d => d.dispose()); + this.disposables = []; + } + async onFileSwitch(doc) { + const fileName = this.getRelativeName(doc); + if (!fileName || this.activeFile === fileName) { + return; + } + this.activeFile = fileName; + this.patches.reset(fileName); + await this.sendSnapshot(doc); + } + scheduleSync(doc, isDirty) { + if (!this.ws.isConnected()) { + return; + } + const fileName = this.getRelativeName(doc); + if (!fileName) { + return; + } + if (!this.debounceTimers.has(fileName)) { + this.sendPatchOrSnapshot(doc, isDirty); + } + const existing = this.debounceTimers.get(fileName); + if (existing) { + clearTimeout(existing); + } + const timer = setTimeout(() => { + this.debounceTimers.delete(fileName); + }, DEBOUNCE_MS); + this.debounceTimers.set(fileName, timer); + } + async sendPatchOrSnapshot(doc, isDirty) { + const fileName = this.getRelativeName(doc); + if (!fileName) { + return; + } + const content = doc.getText(); + const patch = this.patches.encode(fileName, content, isDirty); + if (patch) { + this.ws.send('FILE_PATCH', patch); + } + else { + await this.sendSnapshot(doc); + } + } + async sendSnapshot(doc) { + const fileName = this.getRelativeName(doc); + if (!fileName) { + return; + } + const content = doc.getText(); + const isDirty = doc.isDirty; + const gitHead = await this.git.isTracked(doc.fileName); + const payload = this.snapshot.build(fileName, content, isDirty, gitHead); + this.patches.recordSnapshot(fileName, content, payload.seq); + this.ws.send('FILE_SNAPSHOT', payload); + const editor = vscode.window.activeTextEditor; + if (editor?.document === doc) { + const pos = editor.selection.active; + this.ws.send('EDITOR_FOCUS', { + fileName, + cursorLine: pos.line, + cursorCol: pos.character, + }); + } + } + async handleSnapshotRequest(fileName) { + this.patches.reset(fileName); + this.snapshot.reset(fileName); + const editor = vscode.window.activeTextEditor; + if (editor && this.getRelativeName(editor.document) === fileName) { + await this.sendSnapshot(editor.document); + } + } + getRelativeName(doc) { + if (doc.uri.scheme != 'file') { + return null; + } + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders?.length) { + return null; + } + const root = workspaceFolders[0].uri.fsPath; + return path.relative(root, doc.fileName).replace(/\\/g, '/'); + } +} +exports.FileWatcher = FileWatcher; +//# sourceMappingURL=FileWatcher.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/FileWatcher.js.map b/packages/vscode-extension/dist/diff/FileWatcher.js.map new file mode 100644 index 0000000..05d35be --- /dev/null +++ b/packages/vscode-extension/dist/diff/FileWatcher.js.map @@ -0,0 +1 @@ +{"version":3,"file":"FileWatcher.js","sourceRoot":"","sources":["../../src/diff/FileWatcher.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAgC;AAChC,2CAA4B;AAM5B,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB,MAAa,WAAW;IAKpB,YACqB,GAAyB,EACzB,QAAwB,EACxB,OAAqB,EACrB,EAAY;QAHZ,QAAG,GAAH,GAAG,CAAsB;QACzB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,YAAO,GAAP,OAAO,CAAc;QACrB,OAAE,GAAF,EAAE,CAAU;QARzB,gBAAW,GAAwB,EAAE,CAAC;QACtC,mBAAc,GAAG,IAAI,GAAG,EAA0B,CAAC;QACnD,eAAU,GAAkB,IAAI,CAAC;IAOtC,CAAC;IAEJ,KAAK,CAAC,aAAqB;QACvB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;QAEnC,IAAI,CAAC,WAAW,CAAC,IAAI,CACjB,MAAM,CAAC,MAAM,CAAC,2BAA2B,CAAC,MAAM,CAAC,EAAE;YAC/C,IAAI,MAAM,EAAE,QAAQ,EAAE,CAAC;gBACnB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACvC,CAAC;QACL,CAAC,CAAC,CACL,CAAC;QAEF,IAAI,CAAC,WAAW,CAAC,IAAI,CACjB,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE;YACzC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC,CAAC,CACL,CAAC;QAEF,IAAI,CAAC,WAAW,CAAC,IAAI,CACjB,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAC,KAAK,CAAC,EAAE;YAC7C,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC5C,CAAC,CAAC,CACL,CAAC;QAEF,IAAI,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC;YAC3C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;IAED,IAAI;QACA,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;IAC1B,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,GAAwB;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAE1D,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAEO,YAAY,CAAC,GAAwB,EAAE,OAAgB;QAC3D,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAEvC,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAE1B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,QAAQ,EAAE,CAAC;YAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAAC,CAAC;QAEzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC,EAAE,WAAW,CAAC,CAAC;QAEhB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,GAAwB,EAAE,OAAgB;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAE1B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAE9D,IAAI,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,GAAwB;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAE1B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEvD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAEzE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAE5D,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAC9C,IAAI,MAAM,EAAE,QAAQ,KAAK,GAAG,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YACpC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE;gBACzB,QAAQ;gBACR,UAAU,EAAE,GAAG,CAAC,IAAI;gBACpB,SAAS,EAAE,GAAG,CAAC,SAAS;aAC3B,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,QAAgB;QACxC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAC9C,IAAI,MAAM,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,CAAC;IACL,CAAC;IAEO,eAAe,CAAC,GAAwB;QAC5C,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QAC9C,MAAM,gBAAgB,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC;QAC3D,IAAI,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;QAC5C,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;CACJ;AApID,kCAoIC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/PatchEncoder.d.ts b/packages/vscode-extension/dist/diff/PatchEncoder.d.ts new file mode 100644 index 0000000..5b3c13a --- /dev/null +++ b/packages/vscode-extension/dist/diff/PatchEncoder.d.ts @@ -0,0 +1,17 @@ +export interface PatchPayload { + fileName: string; + patches: string; + fromSeq: number; + toSeq: number; + isDirty: boolean; + timestamp: number; +} +export declare class PatchEncoder { + private readonly engine; + private readonly lastSent; + encode(fileName: string, newContent: string, isDirty: boolean): PatchPayload | null; + recordSnapshot(fileName: string, content: string, seq: number): void; + reset(fileName: string): void; + resetAll(): void; +} +//# sourceMappingURL=PatchEncoder.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/PatchEncoder.d.ts.map b/packages/vscode-extension/dist/diff/PatchEncoder.d.ts.map new file mode 100644 index 0000000..8dad8cd --- /dev/null +++ b/packages/vscode-extension/dist/diff/PatchEncoder.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"PatchEncoder.d.ts","sourceRoot":"","sources":["../../src/diff/PatchEncoder.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuD;IAEhF,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI;IAoBnF,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAIpE,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI7B,QAAQ,IAAI,IAAI;CAGnB"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/PatchEncoder.js b/packages/vscode-extension/dist/diff/PatchEncoder.js new file mode 100644 index 0000000..96b0de4 --- /dev/null +++ b/packages/vscode-extension/dist/diff/PatchEncoder.js @@ -0,0 +1,68 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PatchEncoder = void 0; +const dmp = __importStar(require("diff-match-patch")); +class PatchEncoder { + constructor() { + this.engine = new dmp.diff_match_patch(); + this.lastSent = new Map(); + } + encode(fileName, newContent, isDirty) { + const last = this.lastSent.get(fileName); + if (!last) { + return null; + } + if (last.content === newContent) { + return null; + } + const patches = this.engine.patch_make(last.content, newContent); + const patchText = this.engine.patch_toText(patches); + const toSeq = last.seq + 1; + this.lastSent.set(fileName, { content: newContent, seq: toSeq }); + return { fileName, patches: patchText, fromSeq: last.seq, toSeq: toSeq, isDirty, timestamp: Date.now() }; + } + recordSnapshot(fileName, content, seq) { + this.lastSent.set(fileName, { content, seq }); + } + reset(fileName) { + this.lastSent.delete(fileName); + } + resetAll() { + this.lastSent.clear(); + } +} +exports.PatchEncoder = PatchEncoder; +//# sourceMappingURL=PatchEncoder.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/PatchEncoder.js.map b/packages/vscode-extension/dist/diff/PatchEncoder.js.map new file mode 100644 index 0000000..f4d0706 --- /dev/null +++ b/packages/vscode-extension/dist/diff/PatchEncoder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PatchEncoder.js","sourceRoot":"","sources":["../../src/diff/PatchEncoder.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,sDAAwC;AAWxC,MAAa,YAAY;IAAzB;QACqB,WAAM,GAAG,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACpC,aAAQ,GAAG,IAAI,GAAG,EAA4C,CAAC;IAiCpF,CAAC;IA/BG,MAAM,CAAC,QAAgB,EAAE,UAAkB,EAAE,OAAgB;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEzC,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACjE,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QAE3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAEjE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAC7G,CAAC;IAED,cAAc,CAAC,QAAgB,EAAE,OAAe,EAAE,GAAW;QACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,QAAgB;QAClB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,QAAQ;QACJ,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;CACJ;AAnCD,oCAmCC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/SnapshotEngine.d.ts b/packages/vscode-extension/dist/diff/SnapshotEngine.d.ts new file mode 100644 index 0000000..7ac74c9 --- /dev/null +++ b/packages/vscode-extension/dist/diff/SnapshotEngine.d.ts @@ -0,0 +1,17 @@ +export interface SnapshotPayload { + fileName: string; + content: string; + encoding: 'utf8' | 'gzip+base64'; + seq: number; + isDirty: boolean; + gitHead: boolean; + timestamp: number; +} +export declare class SnapshotEngine { + private seqCounter; + build(fileName: string, content: string, isDirty: boolean, gitHead: boolean): SnapshotPayload; + currentSeq(fileName: string): number; + reset(fileName: string): void; + resetAll(): void; +} +//# sourceMappingURL=SnapshotEngine.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/SnapshotEngine.d.ts.map b/packages/vscode-extension/dist/diff/SnapshotEngine.d.ts.map new file mode 100644 index 0000000..30b3246 --- /dev/null +++ b/packages/vscode-extension/dist/diff/SnapshotEngine.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SnapshotEngine.d.ts","sourceRoot":"","sources":["../../src/diff/SnapshotEngine.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,cAAc;IACvB,OAAO,CAAC,UAAU,CAA6B;IAE/C,KAAK,CACD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,GACjB,eAAe;IAkBlB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAIpC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI7B,QAAQ,IAAI,IAAI;CAGnB"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/SnapshotEngine.js b/packages/vscode-extension/dist/diff/SnapshotEngine.js new file mode 100644 index 0000000..2623178 --- /dev/null +++ b/packages/vscode-extension/dist/diff/SnapshotEngine.js @@ -0,0 +1,69 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SnapshotEngine = void 0; +const zlib = __importStar(require("zlib")); +const COMPRESSION_THRESHOLD_BYTES = 64 * 1024; +class SnapshotEngine { + constructor() { + this.seqCounter = new Map(); + } + build(fileName, content, isDirty, gitHead) { + const seq = (this.seqCounter.get(fileName) ?? 0) + 1; + this.seqCounter.set(fileName, seq); + const rawBytes = Buffer.byteLength(content, 'utf8'); + const shouldCompress = rawBytes > COMPRESSION_THRESHOLD_BYTES; + return { + fileName, + content: shouldCompress ? zlib.gzipSync(content).toString('base64') : content, + encoding: shouldCompress ? 'gzip+base64' : 'utf8', + seq, + isDirty, + gitHead, + timestamp: Date.now(), + }; + } + currentSeq(fileName) { + return this.seqCounter.get(fileName) ?? 0; + } + reset(fileName) { + this.seqCounter.delete(fileName); + } + resetAll() { + this.seqCounter.clear(); + } +} +exports.SnapshotEngine = SnapshotEngine; +//# sourceMappingURL=SnapshotEngine.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/diff/SnapshotEngine.js.map b/packages/vscode-extension/dist/diff/SnapshotEngine.js.map new file mode 100644 index 0000000..db2a264 --- /dev/null +++ b/packages/vscode-extension/dist/diff/SnapshotEngine.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SnapshotEngine.js","sourceRoot":"","sources":["../../src/diff/SnapshotEngine.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA4B;AAE5B,MAAM,2BAA2B,GAAG,EAAE,GAAG,IAAI,CAAC;AAY9C,MAAa,cAAc;IAA3B;QACY,eAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAoCnD,CAAC;IAlCG,KAAK,CACD,QAAgB,EAChB,OAAe,EACf,OAAgB,EAChB,OAAgB;QAEhB,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAEnC,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACnD,MAAM,cAAc,GAAG,QAAQ,GAAG,2BAA2B,CAAC;QAE9D,OAAO;YACH,QAAQ;YACR,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO;YAC7E,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM;YACjD,GAAG;YACH,OAAO;YACP,OAAO;YACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACxB,CAAC;IACN,CAAC;IAED,UAAU,CAAC,QAAgB;QACvB,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,QAAgB;QAClB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,QAAQ;QACJ,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;CACJ;AArCD,wCAqCC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.d.ts b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.d.ts new file mode 100644 index 0000000..752ed64 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.d.ts @@ -0,0 +1,70 @@ +import { IEditorAdapter, EditorCapabilities, DetectionResult, PromptInjectionResult } from './types'; +/** + * Adapter for Antigravity extension. + * + * Antigravity is a closed-source AI code editor, so we can only use public commands + * exposed through VS Code's command API. We cannot read internal state, access + * chat history, or stream tokens in real-time. + * + * Capabilities: + * - Prompt injection: Yes (via antigravity.* commands discovered dynamically) + * - Chat history: No (closed-source, no public API) + * - Token streaming: No (closed-source, no public API) + * - Diff artifacts: No (closed-source, no public API) + * - Prevent auto-apply: No (unknown, depends on Antigravity's internal settings) + * - Sync level: Control-only + * + * Safety: This adapter uses ONLY public VS Code commands. We never: + * - Scrape UI elements or webview DOM + * - Use keystroke replay or automation + * - Access private or undocumented APIs + * - Make assumptions about Antigravity's internal state + * + * The mobile client must reconstruct state from other signals (file changes, + * diff events) rather than relying on chat mirroring. + */ +export declare class AntigravityAdapter implements IEditorAdapter { + readonly editorId = "antigravity"; + readonly editorName = "Antigravity"; + readonly capabilities: EditorCapabilities; + /** + * Detect if Antigravity is installed and available. + * + * Uses command discovery pattern: queries all available VS Code commands + * and looks for Antigravity-specific command patterns (commands starting with + * "antigravity." or containing "antigravity" in the name). + * + * Safety: Never assumes Antigravity is available. Always checks dynamically + * using public VS Code API. Fails safe by returning not installed if + * command query fails. + * + * @returns DetectionResult indicating if Antigravity is installed and available commands + */ + detect(): Promise; + /** + * Inject a prompt into Antigravity's chat panel. + * + * Attempts to use Antigravity's public commands to inject a prompt. Since Antigravity + * is closed-source, we don't know the exact command names, so we try multiple + * common patterns with fallback logic. + * + * Fallback strategy: + * 1. Try 'antigravity.chat.send' (common pattern for chat commands) + * 2. Try 'antigravity.chat.open' with message parameter + * 3. Try 'antigravity.sendMessage' (alternative pattern) + * + * Safety: Uses only public VS Code commands. Never throws exceptions - always + * returns error result with clear message if all attempts fail. The actual + * command names would need to be discovered through Antigravity's documentation + * or command palette. + * + * Note: The command names used here are educated guesses based on common + * patterns. In production, these would need to be verified against Antigravity's + * actual public API. + * + * @param prompt The prompt text to inject into Antigravity's chat + * @returns PromptInjectionResult indicating success or failure with error details + */ + injectPrompt(prompt: string): Promise; +} +//# sourceMappingURL=AntigravityAdapter.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.d.ts.map b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.d.ts.map new file mode 100644 index 0000000..adc2278 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"AntigravityAdapter.d.ts","sourceRoot":"","sources":["../../../src/editors/adapters/AntigravityAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,QAAQ,iBAAiB;IAClC,QAAQ,CAAC,UAAU,iBAAiB;IACpC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAOvC;IAEF;;;;;;;;;;;;OAYG;IACG,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IA6BxC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAqDnE"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.js b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.js new file mode 100644 index 0000000..0e3b181 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.js @@ -0,0 +1,186 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AntigravityAdapter = void 0; +const vscode = __importStar(require("vscode")); +/** + * Adapter for Antigravity extension. + * + * Antigravity is a closed-source AI code editor, so we can only use public commands + * exposed through VS Code's command API. We cannot read internal state, access + * chat history, or stream tokens in real-time. + * + * Capabilities: + * - Prompt injection: Yes (via antigravity.* commands discovered dynamically) + * - Chat history: No (closed-source, no public API) + * - Token streaming: No (closed-source, no public API) + * - Diff artifacts: No (closed-source, no public API) + * - Prevent auto-apply: No (unknown, depends on Antigravity's internal settings) + * - Sync level: Control-only + * + * Safety: This adapter uses ONLY public VS Code commands. We never: + * - Scrape UI elements or webview DOM + * - Use keystroke replay or automation + * - Access private or undocumented APIs + * - Make assumptions about Antigravity's internal state + * + * The mobile client must reconstruct state from other signals (file changes, + * diff events) rather than relying on chat mirroring. + */ +class AntigravityAdapter { + constructor() { + this.editorId = 'antigravity'; + this.editorName = 'Antigravity'; + this.capabilities = { + canInjectPrompt: true, // Assuming Antigravity exposes prompt injection commands + canReadChatHistory: false, // Closed-source: no access to internal state + canStreamAssistantTokens: false, // Closed-source: no access to streaming + canReadDiffArtifacts: false, // Closed-source: no public diff API + canPreventAutoApply: false, // Unknown: depends on Antigravity's settings + syncLevel: 'control-only', // Can only control, cannot read state + }; + // Note: readChatHistory and readDiffArtifacts are NOT implemented + // because Antigravity is closed-source and does not expose these capabilities. + // The capabilities object correctly declares these as false. + } + /** + * Detect if Antigravity is installed and available. + * + * Uses command discovery pattern: queries all available VS Code commands + * and looks for Antigravity-specific command patterns (commands starting with + * "antigravity." or containing "antigravity" in the name). + * + * Safety: Never assumes Antigravity is available. Always checks dynamically + * using public VS Code API. Fails safe by returning not installed if + * command query fails. + * + * @returns DetectionResult indicating if Antigravity is installed and available commands + */ + async detect() { + try { + // Query all available commands from VS Code + const commands = await vscode.commands.getCommands(true); + // Look for Antigravity-specific commands + // Antigravity commands typically start with "antigravity." but we also check + // for commands containing "antigravity" to catch variations + const antigravityCommands = commands.filter((cmd) => cmd.startsWith('antigravity.') || cmd.toLowerCase().includes('antigravity')); + // Antigravity is installed if we find any antigravity-related commands + const isInstalled = antigravityCommands.length > 0; + return { + isInstalled, + availableCommands: antigravityCommands, + }; + } + catch (error) { + // Safety: Fail safe by returning not installed if command query fails + // This ensures we never assume Antigravity is available when we can't verify + return { + isInstalled: false, + availableCommands: [], + }; + } + } + /** + * Inject a prompt into Antigravity's chat panel. + * + * Attempts to use Antigravity's public commands to inject a prompt. Since Antigravity + * is closed-source, we don't know the exact command names, so we try multiple + * common patterns with fallback logic. + * + * Fallback strategy: + * 1. Try 'antigravity.chat.send' (common pattern for chat commands) + * 2. Try 'antigravity.chat.open' with message parameter + * 3. Try 'antigravity.sendMessage' (alternative pattern) + * + * Safety: Uses only public VS Code commands. Never throws exceptions - always + * returns error result with clear message if all attempts fail. The actual + * command names would need to be discovered through Antigravity's documentation + * or command palette. + * + * Note: The command names used here are educated guesses based on common + * patterns. In production, these would need to be verified against Antigravity's + * actual public API. + * + * @param prompt The prompt text to inject into Antigravity's chat + * @returns PromptInjectionResult indicating success or failure with error details + */ + async injectPrompt(prompt) { + // Try primary command pattern: antigravity.chat.send + try { + await vscode.commands.executeCommand('antigravity.chat.send', prompt); + return { + success: true, + commandUsed: 'antigravity.chat.send', + }; + } + catch (primaryError) { + // Primary command failed, try fallback patterns + // Fallback 1: Try antigravity.chat.open with message parameter + try { + await vscode.commands.executeCommand('antigravity.chat.open', { + message: prompt, + }); + return { + success: true, + commandUsed: 'antigravity.chat.open', + }; + } + catch (fallback1Error) { + // Fallback 2: Try antigravity.sendMessage + try { + await vscode.commands.executeCommand('antigravity.sendMessage', prompt); + return { + success: true, + commandUsed: 'antigravity.sendMessage', + }; + } + catch (fallback2Error) { + // All attempts failed - return error result with context + // Safety: Never throw, always return error result + const primaryErrorMessage = primaryError instanceof Error ? primaryError.message : String(primaryError); + return { + success: false, + error: `Failed to inject prompt into Antigravity: ${primaryErrorMessage}. ` + + `Tried commands: antigravity.chat.send, antigravity.chat.open, antigravity.sendMessage. ` + + `Antigravity may not be installed or may use different command names.`, + commandUsed: 'antigravity.chat.send (attempted)', + }; + } + } + } + } +} +exports.AntigravityAdapter = AntigravityAdapter; +//# sourceMappingURL=AntigravityAdapter.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.js.map b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.js.map new file mode 100644 index 0000000..4aeb903 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/AntigravityAdapter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AntigravityAdapter.js","sourceRoot":"","sources":["../../../src/editors/adapters/AntigravityAdapter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAQjC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,kBAAkB;IAA/B;QACW,aAAQ,GAAG,aAAa,CAAC;QACzB,eAAU,GAAG,aAAa,CAAC;QAC3B,iBAAY,GAAuB;YAC1C,eAAe,EAAE,IAAI,EAAE,yDAAyD;YAChF,kBAAkB,EAAE,KAAK,EAAE,6CAA6C;YACxE,wBAAwB,EAAE,KAAK,EAAE,wCAAwC;YACzE,oBAAoB,EAAE,KAAK,EAAE,oCAAoC;YACjE,mBAAmB,EAAE,KAAK,EAAE,6CAA6C;YACzE,SAAS,EAAE,cAAc,EAAE,sCAAsC;SAClE,CAAC;QAsHF,kEAAkE;QAClE,+EAA+E;QAC/E,6DAA6D;IAC/D,CAAC;IAvHC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,4CAA4C;YAC5C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAEzD,yCAAyC;YACzC,6EAA6E;YAC7E,4DAA4D;YAC5D,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CACzC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CACrF,CAAC;YAEF,uEAAuE;YACvE,MAAM,WAAW,GAAG,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC;YAEnD,OAAO;gBACL,WAAW;gBACX,iBAAiB,EAAE,mBAAmB;aACvC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sEAAsE;YACtE,6EAA6E;YAC7E,OAAO;gBACL,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,EAAE;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,YAAY,CAAC,MAAc;QAC/B,qDAAqD;QACrD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;YAEtE,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,WAAW,EAAE,uBAAuB;aACrC,CAAC;QACJ,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACtB,gDAAgD;YAEhD,+DAA+D;YAC/D,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,EAAE;oBAC5D,OAAO,EAAE,MAAM;iBAChB,CAAC,CAAC;gBAEH,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,WAAW,EAAE,uBAAuB;iBACrC,CAAC;YACJ,CAAC;YAAC,OAAO,cAAc,EAAE,CAAC;gBACxB,0CAA0C;gBAC1C,IAAI,CAAC;oBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,yBAAyB,EAAE,MAAM,CAAC,CAAC;oBAExE,OAAO;wBACL,OAAO,EAAE,IAAI;wBACb,WAAW,EAAE,yBAAyB;qBACvC,CAAC;gBACJ,CAAC;gBAAC,OAAO,cAAc,EAAE,CAAC;oBACxB,yDAAyD;oBACzD,kDAAkD;oBAClD,MAAM,mBAAmB,GACvB,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;oBAE9E,OAAO;wBACL,OAAO,EAAE,KAAK;wBACd,KAAK,EACH,6CAA6C,mBAAmB,IAAI;4BACpE,yFAAyF;4BACzF,sEAAsE;wBACxE,WAAW,EAAE,mCAAmC;qBACjD,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CAKF;AAnID,gDAmIC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.d.ts b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.d.ts new file mode 100644 index 0000000..fa3c109 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.d.ts @@ -0,0 +1,23 @@ +import { IEditorAdapter, EditorCapabilities, DetectionResult, PromptInjectionResult, ChatMessage, DiffArtifact } from './types'; +export declare class ContinueAdapter implements IEditorAdapter { + readonly editorId = "continue"; + readonly editorName = "Continue"; + readonly capabilities: EditorCapabilities; + detect(): Promise; + injectPrompt(prompt: string): Promise; + readChatHistory(): Promise; + /** + * Read diff artifacts from Continue. + * + * Note: This method is declared because Continue supports reading diffs + * (it's open-source and exposes diff state). However, the actual implementation + * depends on Continue's specific API for accessing diff artifacts. + * + * Safety: Only reads diffs when Continue provides public access. + * + * @returns Array of diff artifacts + * @throws Error indicating not yet implemented + */ + readDiffArtifacts(): Promise; +} +//# sourceMappingURL=ContinueAdapter.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.d.ts.map b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.d.ts.map new file mode 100644 index 0000000..992f63b --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ContinueAdapter.d.ts","sourceRoot":"","sources":["../../../src/editors/adapters/ContinueAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,WAAW,EACX,YAAY,EACb,MAAM,SAAS,CAAC;AAEjB,qBAAa,eAAgB,YAAW,cAAc;IACpD,QAAQ,CAAC,QAAQ,cAAc;IAC/B,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAOvC;IAEI,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IAqBlC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAsC5D,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAO/C;;;;;;;;;;;OAWG;IACG,iBAAiB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAKnD"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.js b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.js new file mode 100644 index 0000000..261f518 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.js @@ -0,0 +1,134 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContinueAdapter = void 0; +const vscode = __importStar(require("vscode")); +class ContinueAdapter { + constructor() { + this.editorId = 'continue'; + this.editorName = 'Continue'; + this.capabilities = { + canInjectPrompt: true, + canReadChatHistory: true, + canStreamAssistantTokens: true, + canReadDiffArtifacts: true, + canPreventAutoApply: true, + syncLevel: 'full', + }; + } + async detect() { + try { + const commands = await vscode.commands.getCommands(true); + const continueCommands = commands.filter((cmd) => cmd.startsWith('continue.')); + const isInstalled = continueCommands.length > 0; + return { + isInstalled, + availableCommands: continueCommands, + }; + } + catch (error) { + // Safety: Fail safe by returning not installed + return { + isInstalled: false, + availableCommands: [], + }; + } + } + async injectPrompt(prompt) { + try { + await vscode.commands.executeCommand('continue.continueGUIView.focusContinueInput', { + text: prompt, + }); + return { + success: true, + commandUsed: 'continue.continueGUIView.focusContinueInput', + }; + } + catch (error) { + // Safety: Never throw, always return error result with context + // Handle any type of error safely, including objects with malformed toString + let errorMessage; + try { + if (error instanceof Error) { + errorMessage = error.message; + } + else if (typeof error === 'string') { + errorMessage = error; + } + else if (error && typeof error === 'object') { + // Safely handle objects that might have broken toString + errorMessage = JSON.stringify(error); + } + else { + errorMessage = String(error); + } + } + catch { + // If even JSON.stringify fails, use a fallback + errorMessage = 'Unknown error occurred'; + } + return { + success: false, + error: `Failed to inject prompt into Continue: ${errorMessage}`, + commandUsed: 'continue.continueGUIView.focusContinueInput', + }; + } + } + async readChatHistory() { + // Implementation would access Continue's internal state + // This is allowed because Continue is open-source + // Actual implementation depends on Continue's API + throw new Error('readChatHistory not yet implemented for Continue adapter'); + } + /** + * Read diff artifacts from Continue. + * + * Note: This method is declared because Continue supports reading diffs + * (it's open-source and exposes diff state). However, the actual implementation + * depends on Continue's specific API for accessing diff artifacts. + * + * Safety: Only reads diffs when Continue provides public access. + * + * @returns Array of diff artifacts + * @throws Error indicating not yet implemented + */ + async readDiffArtifacts() { + // Implementation would access Continue's diff state + // This is allowed because Continue is open-source + throw new Error('readDiffArtifacts not yet implemented for Continue adapter'); + } +} +exports.ContinueAdapter = ContinueAdapter; +//# sourceMappingURL=ContinueAdapter.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.js.map b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.js.map new file mode 100644 index 0000000..c54fade --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/ContinueAdapter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ContinueAdapter.js","sourceRoot":"","sources":["../../../src/editors/adapters/ContinueAdapter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAUjC,MAAa,eAAe;IAA5B;QACW,aAAQ,GAAG,UAAU,CAAC;QACtB,eAAU,GAAG,UAAU,CAAC;QACxB,iBAAY,GAAuB;YAC1C,eAAe,EAAE,IAAI;YACrB,kBAAkB,EAAE,IAAI;YACxB,wBAAwB,EAAE,IAAI;YAC9B,oBAAoB,EAAE,IAAI;YAC1B,mBAAmB,EAAE,IAAI;YACzB,SAAS,EAAE,MAAM;SAClB,CAAC;IAqFJ,CAAC;IAnFC,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAEzD,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;YAE/E,MAAM,WAAW,GAAG,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;YAEhD,OAAO;gBACL,WAAW;gBACX,iBAAiB,EAAE,gBAAgB;aACpC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,+CAA+C;YAC/C,OAAO;gBACL,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,EAAE;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAc;QAC/B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,6CAA6C,EAAE;gBAClF,IAAI,EAAE,MAAM;aACb,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,WAAW,EAAE,6CAA6C;aAC3D,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,+DAA+D;YAC/D,6EAA6E;YAC7E,IAAI,YAAoB,CAAC;YACzB,IAAI,CAAC;gBACH,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;oBAC3B,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC/B,CAAC;qBAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACrC,YAAY,GAAG,KAAK,CAAC;gBACvB,CAAC;qBAAM,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC9C,wDAAwD;oBACxD,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBACvC,CAAC;qBAAM,CAAC;oBACN,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,+CAA+C;gBAC/C,YAAY,GAAG,wBAAwB,CAAC;YAC1C,CAAC;YAED,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,0CAA0C,YAAY,EAAE;gBAC/D,WAAW,EAAE,6CAA6C;aAC3D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,wDAAwD;QACxD,kDAAkD;QAClD,kDAAkD;QAClD,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,iBAAiB;QACrB,oDAAoD;QACpD,kDAAkD;QAClD,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;CACF;AA/FD,0CA+FC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/CursorAdapter.d.ts b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.d.ts new file mode 100644 index 0000000..428b868 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.d.ts @@ -0,0 +1,70 @@ +import { IEditorAdapter, EditorCapabilities, DetectionResult, PromptInjectionResult } from './types'; +/** + * Adapter for Cursor extension. + * + * Cursor is a closed-source AI code editor, so we can only use public commands + * exposed through VS Code's command API. We cannot read internal state, access + * chat history, or stream tokens in real-time. + * + * Capabilities: + * - Prompt injection: Yes (via cursor.* commands discovered dynamically) + * - Chat history: No (closed-source, no public API) + * - Token streaming: No (closed-source, no public API) + * - Diff artifacts: No (closed-source, no public API) + * - Prevent auto-apply: No (unknown, depends on Cursor's internal settings) + * - Sync level: Control-only + * + * Safety: This adapter uses ONLY public VS Code commands. We never: + * - Scrape UI elements or webview DOM + * - Use keystroke replay or automation + * - Access private or undocumented APIs + * - Make assumptions about Cursor's internal state + * + * The mobile client must reconstruct state from other signals (file changes, + * diff events) rather than relying on chat mirroring. + */ +export declare class CursorAdapter implements IEditorAdapter { + readonly editorId = "cursor"; + readonly editorName = "Cursor"; + readonly capabilities: EditorCapabilities; + /** + * Detect if Cursor is installed and available. + * + * Uses command discovery pattern: queries all available VS Code commands + * and looks for Cursor-specific command patterns (commands starting with + * "cursor." or containing "cursor" in the name). + * + * Safety: Never assumes Cursor is available. Always checks dynamically + * using public VS Code API. Fails safe by returning not installed if + * command query fails. + * + * @returns DetectionResult indicating if Cursor is installed and available commands + */ + detect(): Promise; + /** + * Inject a prompt into Cursor's chat panel. + * + * Attempts to use Cursor's public commands to inject a prompt. Since Cursor + * is closed-source, we don't know the exact command names, so we try multiple + * common patterns with fallback logic. + * + * Fallback strategy: + * 1. Try 'cursor.chat.newMessage' (common pattern for chat commands) + * 2. Try 'cursor.openChat' with message parameter + * 3. Try 'cursor.sendMessage' (alternative pattern) + * + * Safety: Uses only public VS Code commands. Never throws exceptions - always + * returns error result with clear message if all attempts fail. The actual + * command names would need to be discovered through Cursor's documentation + * or command palette. + * + * Note: The command names used here are educated guesses based on common + * patterns. In production, these would need to be verified against Cursor's + * actual public API. + * + * @param prompt The prompt text to inject into Cursor's chat + * @returns PromptInjectionResult indicating success or failure with error details + */ + injectPrompt(prompt: string): Promise; +} +//# sourceMappingURL=CursorAdapter.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/CursorAdapter.d.ts.map b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.d.ts.map new file mode 100644 index 0000000..56a5028 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"CursorAdapter.d.ts","sourceRoot":"","sources":["../../../src/editors/adapters/CursorAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD,QAAQ,CAAC,QAAQ,YAAY;IAC7B,QAAQ,CAAC,UAAU,YAAY;IAC/B,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAOvC;IAEF;;;;;;;;;;;;OAYG;IACG,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IA6BxC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAqDnE"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/CursorAdapter.js b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.js new file mode 100644 index 0000000..9cf86f5 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.js @@ -0,0 +1,186 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CursorAdapter = void 0; +const vscode = __importStar(require("vscode")); +/** + * Adapter for Cursor extension. + * + * Cursor is a closed-source AI code editor, so we can only use public commands + * exposed through VS Code's command API. We cannot read internal state, access + * chat history, or stream tokens in real-time. + * + * Capabilities: + * - Prompt injection: Yes (via cursor.* commands discovered dynamically) + * - Chat history: No (closed-source, no public API) + * - Token streaming: No (closed-source, no public API) + * - Diff artifacts: No (closed-source, no public API) + * - Prevent auto-apply: No (unknown, depends on Cursor's internal settings) + * - Sync level: Control-only + * + * Safety: This adapter uses ONLY public VS Code commands. We never: + * - Scrape UI elements or webview DOM + * - Use keystroke replay or automation + * - Access private or undocumented APIs + * - Make assumptions about Cursor's internal state + * + * The mobile client must reconstruct state from other signals (file changes, + * diff events) rather than relying on chat mirroring. + */ +class CursorAdapter { + constructor() { + this.editorId = 'cursor'; + this.editorName = 'Cursor'; + this.capabilities = { + canInjectPrompt: true, // Assuming Cursor exposes prompt injection commands + canReadChatHistory: false, // Closed-source: no access to internal state + canStreamAssistantTokens: false, // Closed-source: no access to streaming + canReadDiffArtifacts: false, // Closed-source: no public diff API + canPreventAutoApply: false, // Unknown: depends on Cursor's settings + syncLevel: 'control-only', // Can only control, cannot read state + }; + // Note: readChatHistory and readDiffArtifacts are NOT implemented + // because Cursor is closed-source and does not expose these capabilities. + // The capabilities object correctly declares these as false. + } + /** + * Detect if Cursor is installed and available. + * + * Uses command discovery pattern: queries all available VS Code commands + * and looks for Cursor-specific command patterns (commands starting with + * "cursor." or containing "cursor" in the name). + * + * Safety: Never assumes Cursor is available. Always checks dynamically + * using public VS Code API. Fails safe by returning not installed if + * command query fails. + * + * @returns DetectionResult indicating if Cursor is installed and available commands + */ + async detect() { + try { + // Query all available commands from VS Code + const commands = await vscode.commands.getCommands(true); + // Look for Cursor-specific commands + // Cursor commands typically start with "cursor." but we also check + // for commands containing "cursor" to catch variations + const cursorCommands = commands.filter((cmd) => cmd.startsWith('cursor.') || cmd.toLowerCase().includes('cursor')); + // Cursor is installed if we find any cursor-related commands + const isInstalled = cursorCommands.length > 0; + return { + isInstalled, + availableCommands: cursorCommands, + }; + } + catch (error) { + // Safety: Fail safe by returning not installed if command query fails + // This ensures we never assume Cursor is available when we can't verify + return { + isInstalled: false, + availableCommands: [], + }; + } + } + /** + * Inject a prompt into Cursor's chat panel. + * + * Attempts to use Cursor's public commands to inject a prompt. Since Cursor + * is closed-source, we don't know the exact command names, so we try multiple + * common patterns with fallback logic. + * + * Fallback strategy: + * 1. Try 'cursor.chat.newMessage' (common pattern for chat commands) + * 2. Try 'cursor.openChat' with message parameter + * 3. Try 'cursor.sendMessage' (alternative pattern) + * + * Safety: Uses only public VS Code commands. Never throws exceptions - always + * returns error result with clear message if all attempts fail. The actual + * command names would need to be discovered through Cursor's documentation + * or command palette. + * + * Note: The command names used here are educated guesses based on common + * patterns. In production, these would need to be verified against Cursor's + * actual public API. + * + * @param prompt The prompt text to inject into Cursor's chat + * @returns PromptInjectionResult indicating success or failure with error details + */ + async injectPrompt(prompt) { + // Try primary command pattern: cursor.chat.newMessage + try { + await vscode.commands.executeCommand('cursor.chat.newMessage', prompt); + return { + success: true, + commandUsed: 'cursor.chat.newMessage', + }; + } + catch (primaryError) { + // Primary command failed, try fallback patterns + // Fallback 1: Try cursor.openChat with message parameter + try { + await vscode.commands.executeCommand('cursor.openChat', { + message: prompt, + }); + return { + success: true, + commandUsed: 'cursor.openChat', + }; + } + catch (fallback1Error) { + // Fallback 2: Try cursor.sendMessage + try { + await vscode.commands.executeCommand('cursor.sendMessage', prompt); + return { + success: true, + commandUsed: 'cursor.sendMessage', + }; + } + catch (fallback2Error) { + // All attempts failed - return error result with context + // Safety: Never throw, always return error result + const primaryErrorMessage = primaryError instanceof Error ? primaryError.message : String(primaryError); + return { + success: false, + error: `Failed to inject prompt into Cursor: ${primaryErrorMessage}. ` + + `Tried commands: cursor.chat.newMessage, cursor.openChat, cursor.sendMessage. ` + + `Cursor may not be installed or may use different command names.`, + commandUsed: 'cursor.chat.newMessage (attempted)', + }; + } + } + } + } +} +exports.CursorAdapter = CursorAdapter; +//# sourceMappingURL=CursorAdapter.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/CursorAdapter.js.map b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.js.map new file mode 100644 index 0000000..4204ab8 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/CursorAdapter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CursorAdapter.js","sourceRoot":"","sources":["../../../src/editors/adapters/CursorAdapter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAQjC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,aAAa;IAA1B;QACW,aAAQ,GAAG,QAAQ,CAAC;QACpB,eAAU,GAAG,QAAQ,CAAC;QACtB,iBAAY,GAAuB;YAC1C,eAAe,EAAE,IAAI,EAAE,oDAAoD;YAC3E,kBAAkB,EAAE,KAAK,EAAE,6CAA6C;YACxE,wBAAwB,EAAE,KAAK,EAAE,wCAAwC;YACzE,oBAAoB,EAAE,KAAK,EAAE,oCAAoC;YACjE,mBAAmB,EAAE,KAAK,EAAE,wCAAwC;YACpE,SAAS,EAAE,cAAc,EAAE,sCAAsC;SAClE,CAAC;QAsHF,kEAAkE;QAClE,0EAA0E;QAC1E,6DAA6D;IAC/D,CAAC;IAvHC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,4CAA4C;YAC5C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAEzD,oCAAoC;YACpC,mEAAmE;YACnE,uDAAuD;YACvD,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CACpC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC3E,CAAC;YAEF,6DAA6D;YAC7D,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;YAE9C,OAAO;gBACL,WAAW;gBACX,iBAAiB,EAAE,cAAc;aAClC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sEAAsE;YACtE,wEAAwE;YACxE,OAAO;gBACL,WAAW,EAAE,KAAK;gBAClB,iBAAiB,EAAE,EAAE;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,YAAY,CAAC,MAAc;QAC/B,sDAAsD;QACtD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;YAEvE,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,WAAW,EAAE,wBAAwB;aACtC,CAAC;QACJ,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACtB,gDAAgD;YAEhD,yDAAyD;YACzD,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,iBAAiB,EAAE;oBACtD,OAAO,EAAE,MAAM;iBAChB,CAAC,CAAC;gBAEH,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,WAAW,EAAE,iBAAiB;iBAC/B,CAAC;YACJ,CAAC;YAAC,OAAO,cAAc,EAAE,CAAC;gBACxB,qCAAqC;gBACrC,IAAI,CAAC;oBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;oBAEnE,OAAO;wBACL,OAAO,EAAE,IAAI;wBACb,WAAW,EAAE,oBAAoB;qBAClC,CAAC;gBACJ,CAAC;gBAAC,OAAO,cAAc,EAAE,CAAC;oBACxB,yDAAyD;oBACzD,kDAAkD;oBAClD,MAAM,mBAAmB,GACvB,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;oBAE9E,OAAO;wBACL,OAAO,EAAE,KAAK;wBACd,KAAK,EACH,wCAAwC,mBAAmB,IAAI;4BAC/D,+EAA+E;4BAC/E,iEAAiE;wBACnE,WAAW,EAAE,oCAAoC;qBAClD,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CAKF;AAnID,sCAmIC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/EditorRegistry.d.ts b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.d.ts new file mode 100644 index 0000000..571c171 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.d.ts @@ -0,0 +1,54 @@ +import { IEditorAdapter, DetectionResult } from './types'; +/** + * Registry for managing editor adapters. + * + * Responsibilities: + * - Register adapters at extension activation + * - Detect available editors + * - Select best adapter based on capabilities + * - Provide query interface + * + * Safety: Registry never assumes an editor is available. All operations + * check adapter capabilities before attempting operations. + */ +export declare class EditorRegistry { + private adapters; + private detectionCache; + /** + * Register an editor adapter. + * + * Called during extension activation to register all known adapters. + */ + register(adapter: IEditorAdapter): void; + /** + * Detect all available editors. + * + * Returns map of editorId -> DetectionResult. + * Results are cached to avoid repeated command queries. + */ + detectAll(): Promise>; + /** + * Get the best available adapter based on capabilities. + * + * Preference order: + * 1. Full sync (Continue) + * 2. Partial sync (Kiro) + * 3. Control-only (Cursor, Antigravity) + * + * Returns null if no editor is installed. + */ + getBestAdapter(): Promise; + /** + * Get adapter by ID. + */ + getAdapter(editorId: string): IEditorAdapter | undefined; + /** + * Get all registered adapters. + */ + getAllAdapters(): IEditorAdapter[]; + /** + * Clear detection cache (useful for testing or manual refresh). + */ + clearCache(): void; +} +//# sourceMappingURL=EditorRegistry.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/EditorRegistry.d.ts.map b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.d.ts.map new file mode 100644 index 0000000..f7be828 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EditorRegistry.d.ts","sourceRoot":"","sources":["../../../src/editors/adapters/EditorRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAsB,MAAM,SAAS,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,cAAc,CAA2C;IAEjE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIvC;;;;;OAKG;IACG,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAmBxD;;;;;;;;;OASG;IACG,cAAc,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IA2BtD;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIxD;;OAEG;IACH,cAAc,IAAI,cAAc,EAAE;IAIlC;;OAEG;IACH,UAAU,IAAI,IAAI;CAGnB"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/EditorRegistry.js b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.js new file mode 100644 index 0000000..c3f198c --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.js @@ -0,0 +1,96 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EditorRegistry = void 0; +/** + * Registry for managing editor adapters. + * + * Responsibilities: + * - Register adapters at extension activation + * - Detect available editors + * - Select best adapter based on capabilities + * - Provide query interface + * + * Safety: Registry never assumes an editor is available. All operations + * check adapter capabilities before attempting operations. + */ +class EditorRegistry { + constructor() { + this.adapters = new Map(); + this.detectionCache = new Map(); + } + /** + * Register an editor adapter. + * + * Called during extension activation to register all known adapters. + */ + register(adapter) { + this.adapters.set(adapter.editorId, adapter); + } + /** + * Detect all available editors. + * + * Returns map of editorId -> DetectionResult. + * Results are cached to avoid repeated command queries. + */ + async detectAll() { + const results = new Map(); + for (const [id, adapter] of this.adapters) { + // Check cache first + if (this.detectionCache.has(id)) { + results.set(id, this.detectionCache.get(id)); + continue; + } + // Detect and cache + const result = await adapter.detect(); + this.detectionCache.set(id, result); + results.set(id, result); + } + return results; + } + /** + * Get the best available adapter based on capabilities. + * + * Preference order: + * 1. Full sync (Continue) + * 2. Partial sync (Kiro) + * 3. Control-only (Cursor, Antigravity) + * + * Returns null if no editor is installed. + */ + async getBestAdapter() { + const available = await this.detectAll(); + // Filter to installed editors + const installed = Array.from(this.adapters.values()).filter((adapter) => available.get(adapter.editorId)?.isInstalled); + if (installed.length === 0) { + return null; + } + // Sort by sync level preference + const syncLevelPriority = { + full: 3, + partial: 2, + 'control-only': 1, + }; + installed.sort((a, b) => syncLevelPriority[b.capabilities.syncLevel] - syncLevelPriority[a.capabilities.syncLevel]); + return installed[0]; + } + /** + * Get adapter by ID. + */ + getAdapter(editorId) { + return this.adapters.get(editorId); + } + /** + * Get all registered adapters. + */ + getAllAdapters() { + return Array.from(this.adapters.values()); + } + /** + * Clear detection cache (useful for testing or manual refresh). + */ + clearCache() { + this.detectionCache.clear(); + } +} +exports.EditorRegistry = EditorRegistry; +//# sourceMappingURL=EditorRegistry.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/EditorRegistry.js.map b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.js.map new file mode 100644 index 0000000..925e43f --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/EditorRegistry.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EditorRegistry.js","sourceRoot":"","sources":["../../../src/editors/adapters/EditorRegistry.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;GAWG;AACH,MAAa,cAAc;IAA3B;QACU,aAAQ,GAAgC,IAAI,GAAG,EAAE,CAAC;QAClD,mBAAc,GAAiC,IAAI,GAAG,EAAE,CAAC;IA6FnE,CAAC;IA3FC;;;;OAIG;IACH,QAAQ,CAAC,OAAuB;QAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;QAEnD,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1C,oBAAoB;YACpB,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC,CAAC;gBAC9C,SAAS;YACX,CAAC;YAED,mBAAmB;YACnB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;YACtC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc;QAClB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAEzC,8BAA8B;QAC9B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACzD,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,WAAW,CAC1D,CAAC;QAEF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,gCAAgC;QAChC,MAAM,iBAAiB,GAAoD;YACzE,IAAI,EAAE,CAAC;YACP,OAAO,EAAE,CAAC;YACV,cAAc,EAAE,CAAC;SAClB,CAAC;QAEF,SAAS,CAAC,IAAI,CACZ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,iBAAiB,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,CAC5F,CAAC;QAEF,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,QAAgB;QACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;CACF;AA/FD,wCA+FC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/KiroAdapter.d.ts b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.d.ts new file mode 100644 index 0000000..c0b185d --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.d.ts @@ -0,0 +1,74 @@ +import { IEditorAdapter, EditorCapabilities, DetectionResult, PromptInjectionResult, ChatMessage, DiffArtifact } from './types'; +/** + * Adapter for Kiro extension. + * + * Since we ARE Kiro, we have internal access to our own state and capabilities. + * However, this adapter uses public command patterns where possible to demonstrate + * the adapter pattern for other editors. + * + * Capabilities: + * - Prompt injection: Yes (we control our own chat interface) + * - Chat history: Yes (we control our own state) + * - Token streaming: Yes (we control our own streaming) + * - Diff artifacts: Yes (we control our own diffs) + * - Prevent auto-apply: Yes (we control edit application) + * - Sync level: Partial (we have full internal access but use partial for consistency) + * + * Safety: Uses public VS Code commands where available. Since we are Kiro, + * we can safely access internal state when needed, but we prefer command-based + * interaction to maintain consistency with other adapters. + */ +export declare class KiroAdapter implements IEditorAdapter { + readonly editorId = "kiro"; + readonly editorName = "Kiro"; + readonly capabilities: EditorCapabilities; + /** + * Detect if Kiro is available. + * + * Since we ARE Kiro, this always returns installed. However, we still + * query for Kiro commands to maintain consistency with other adapters + * and to provide useful debugging information. + * + * Safety: Always returns installed since we are running as Kiro. + * Command discovery is used for informational purposes only. + */ + detect(): Promise; + /** + * Inject a prompt into Kiro's chat panel. + * + * Uses Kiro's public command to send a message to the chat interface. + * The command name follows the pattern used by other AI editors. + * + * Safety: Uses public VS Code command API. Falls back gracefully if + * the command doesn't exist or fails to execute. + * + * @param prompt The prompt text to inject + * @returns Result indicating success or failure with error details + */ + injectPrompt(prompt: string): Promise; + /** + * Read chat history from Kiro. + * + * Since we ARE Kiro, we can access our internal chat state directly. + * This method would integrate with Kiro's chat history storage. + * + * Safety: Only reads our own internal state. No external API access required. + * + * @returns Array of chat messages + * @throws Error indicating not yet implemented + */ + readChatHistory(): Promise; + /** + * Read diff artifacts from Kiro. + * + * Since we ARE Kiro, we can access our internal diff state directly. + * This method would integrate with Kiro's diff tracking system. + * + * Safety: Only reads our own internal state. No external API access required. + * + * @returns Array of diff artifacts + * @throws Error indicating not yet implemented + */ + readDiffArtifacts(): Promise; +} +//# sourceMappingURL=KiroAdapter.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/KiroAdapter.d.ts.map b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.d.ts.map new file mode 100644 index 0000000..f74e3ac --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"KiroAdapter.d.ts","sourceRoot":"","sources":["../../../src/editors/adapters/KiroAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,WAAW,EACX,YAAY,EACb,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,WAAY,YAAW,cAAc;IAChD,QAAQ,CAAC,QAAQ,UAAU;IAC3B,QAAQ,CAAC,UAAU,UAAU;IAC7B,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAOvC;IAEF;;;;;;;;;OASG;IACG,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IAoBxC;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAwBlE;;;;;;;;;;OAUG;IACG,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAM/C;;;;;;;;;;OAUG;IACG,iBAAiB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAKnD"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/KiroAdapter.js b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.js new file mode 100644 index 0000000..e6399e6 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.js @@ -0,0 +1,167 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.KiroAdapter = void 0; +const vscode = __importStar(require("vscode")); +/** + * Adapter for Kiro extension. + * + * Since we ARE Kiro, we have internal access to our own state and capabilities. + * However, this adapter uses public command patterns where possible to demonstrate + * the adapter pattern for other editors. + * + * Capabilities: + * - Prompt injection: Yes (we control our own chat interface) + * - Chat history: Yes (we control our own state) + * - Token streaming: Yes (we control our own streaming) + * - Diff artifacts: Yes (we control our own diffs) + * - Prevent auto-apply: Yes (we control edit application) + * - Sync level: Partial (we have full internal access but use partial for consistency) + * + * Safety: Uses public VS Code commands where available. Since we are Kiro, + * we can safely access internal state when needed, but we prefer command-based + * interaction to maintain consistency with other adapters. + */ +class KiroAdapter { + constructor() { + this.editorId = 'kiro'; + this.editorName = 'Kiro'; + this.capabilities = { + canInjectPrompt: true, + canReadChatHistory: true, // We control our own state + canStreamAssistantTokens: true, // We control our own streaming + canReadDiffArtifacts: true, // We control our own diffs + canPreventAutoApply: true, // We control edit application + syncLevel: 'partial', + }; + } + /** + * Detect if Kiro is available. + * + * Since we ARE Kiro, this always returns installed. However, we still + * query for Kiro commands to maintain consistency with other adapters + * and to provide useful debugging information. + * + * Safety: Always returns installed since we are running as Kiro. + * Command discovery is used for informational purposes only. + */ + async detect() { + try { + // Query for Kiro-specific commands + const commands = await vscode.commands.getCommands(true); + const kiroCommands = commands.filter((cmd) => cmd.startsWith('kiro.')); + // We are always installed since we ARE Kiro + return { + isInstalled: true, + availableCommands: kiroCommands, + }; + } + catch (error) { + // Safety: Even if command query fails, we know we're installed + return { + isInstalled: true, + availableCommands: [], + }; + } + } + /** + * Inject a prompt into Kiro's chat panel. + * + * Uses Kiro's public command to send a message to the chat interface. + * The command name follows the pattern used by other AI editors. + * + * Safety: Uses public VS Code command API. Falls back gracefully if + * the command doesn't exist or fails to execute. + * + * @param prompt The prompt text to inject + * @returns Result indicating success or failure with error details + */ + async injectPrompt(prompt) { + try { + // Use Kiro's public command to send a message + // This command should be registered by the Kiro extension + await vscode.commands.executeCommand('kiro.chat.sendMessage', { + message: prompt, + }); + return { + success: true, + commandUsed: 'kiro.chat.sendMessage', + }; + } + catch (error) { + // Safety: Never throw, always return error result with context + const errorMessage = error instanceof Error ? error.message : String(error); + return { + success: false, + error: `Failed to inject prompt into Kiro: ${errorMessage}`, + commandUsed: 'kiro.chat.sendMessage', + }; + } + } + /** + * Read chat history from Kiro. + * + * Since we ARE Kiro, we can access our internal chat state directly. + * This method would integrate with Kiro's chat history storage. + * + * Safety: Only reads our own internal state. No external API access required. + * + * @returns Array of chat messages + * @throws Error indicating not yet implemented + */ + async readChatHistory() { + // Implementation would access Kiro's internal chat state + // This is allowed because we are Kiro and control our own state + throw new Error('readChatHistory not yet implemented for Kiro adapter'); + } + /** + * Read diff artifacts from Kiro. + * + * Since we ARE Kiro, we can access our internal diff state directly. + * This method would integrate with Kiro's diff tracking system. + * + * Safety: Only reads our own internal state. No external API access required. + * + * @returns Array of diff artifacts + * @throws Error indicating not yet implemented + */ + async readDiffArtifacts() { + // Implementation would access Kiro's internal diff state + // This is allowed because we are Kiro and control our own state + throw new Error('readDiffArtifacts not yet implemented for Kiro adapter'); + } +} +exports.KiroAdapter = KiroAdapter; +//# sourceMappingURL=KiroAdapter.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/KiroAdapter.js.map b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.js.map new file mode 100644 index 0000000..f8440cd --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/KiroAdapter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"KiroAdapter.js","sourceRoot":"","sources":["../../../src/editors/adapters/KiroAdapter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAUjC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAa,WAAW;IAAxB;QACW,aAAQ,GAAG,MAAM,CAAC;QAClB,eAAU,GAAG,MAAM,CAAC;QACpB,iBAAY,GAAuB;YAC1C,eAAe,EAAE,IAAI;YACrB,kBAAkB,EAAE,IAAI,EAAE,2BAA2B;YACrD,wBAAwB,EAAE,IAAI,EAAE,+BAA+B;YAC/D,oBAAoB,EAAE,IAAI,EAAE,2BAA2B;YACvD,mBAAmB,EAAE,IAAI,EAAE,8BAA8B;YACzD,SAAS,EAAE,SAAS;SACrB,CAAC;IAqGJ,CAAC;IAnGC;;;;;;;;;OASG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,mCAAmC;YACnC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACzD,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;YAEvE,4CAA4C;YAC5C,OAAO;gBACL,WAAW,EAAE,IAAI;gBACjB,iBAAiB,EAAE,YAAY;aAChC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,+DAA+D;YAC/D,OAAO;gBACL,WAAW,EAAE,IAAI;gBACjB,iBAAiB,EAAE,EAAE;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,YAAY,CAAC,MAAc;QAC/B,IAAI,CAAC;YACH,8CAA8C;YAC9C,0DAA0D;YAC1D,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,EAAE;gBAC5D,OAAO,EAAE,MAAM;aAChB,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,WAAW,EAAE,uBAAuB;aACrC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,+DAA+D;YAC/D,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE5E,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,sCAAsC,YAAY,EAAE;gBAC3D,WAAW,EAAE,uBAAuB;aACrC,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,eAAe;QACnB,yDAAyD;QACzD,gEAAgE;QAChE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,iBAAiB;QACrB,yDAAyD;QACzD,gEAAgE;QAChE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;CACF;AA/GD,kCA+GC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/errors.d.ts b/packages/vscode-extension/dist/editors/adapters/errors.d.ts new file mode 100644 index 0000000..b522f1f --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/errors.d.ts @@ -0,0 +1,95 @@ +import { IEditorAdapter, PromptInjectionResult } from './types'; +/** + * Error types for editor adapter operations. + */ +export declare enum EditorAdapterErrorType { + /** Command execution failed */ + COMMAND_EXECUTION_FAILED = "COMMAND_EXECUTION_FAILED", + /** Editor not installed or not detected */ + EDITOR_NOT_FOUND = "EDITOR_NOT_FOUND", + /** Operation not supported by this editor */ + UNSUPPORTED_OPERATION = "UNSUPPORTED_OPERATION", + /** Capability check failed */ + CAPABILITY_CHECK_FAILED = "CAPABILITY_CHECK_FAILED", + /** Unknown or unexpected error */ + UNKNOWN_ERROR = "UNKNOWN_ERROR" +} +/** + * Structured error result for adapter operations. + */ +export interface EditorAdapterError { + /** Whether the operation succeeded */ + success: false; + /** Human-readable error message */ + error: string; + /** Machine-readable error code */ + errorCode: EditorAdapterErrorType; + /** Additional context for debugging */ + details?: unknown; +} +/** + * Safe command execution wrapper that catches exceptions and returns error results. + * + * Safety: Never throws exceptions. Always returns a PromptInjectionResult with + * clear error messages if the command fails. + * + * @param command - VS Code command to execute + * @param args - Arguments to pass to the command + * @returns PromptInjectionResult indicating success or failure + */ +export declare function safeExecuteCommand(command: string, ...args: unknown[]): Promise; +/** + * Check if an adapter has a specific capability before attempting an operation. + * + * Safety: Prevents invalid operations by checking capabilities first. + * Throws a clear error if the capability is not supported. + * + * @param adapter - The editor adapter to check + * @param capability - The capability key to check + * @param operationName - Human-readable name of the operation (for error messages) + * @throws Error if the capability is not supported + */ +export declare function requireCapability(adapter: IEditorAdapter, capability: keyof IEditorAdapter['capabilities'], operationName: string): void; +/** + * Check if an adapter has implemented an optional method. + * + * Safety: Ensures that adapters claiming a capability actually implement + * the corresponding method. + * + * @param adapter - The editor adapter to check + * @param methodName - The method name to check + * @param capability - The capability that should enable this method + * @throws Error if the method is not implemented despite capability being true + */ +export declare function requireMethod(adapter: IEditorAdapter, methodName: keyof IEditorAdapter, capability: keyof IEditorAdapter['capabilities']): void; +/** + * Format an error message with context about the adapter and operation. + * + * @param adapter - The editor adapter that encountered the error + * @param operation - The operation that failed + * @param error - The error that occurred + * @returns Formatted error message with full context + */ +export declare function formatAdapterError(adapter: IEditorAdapter, operation: string, error: unknown): string; +/** + * Create a structured error result for adapter operations. + * + * @param errorType - The type of error that occurred + * @param message - Human-readable error message + * @param details - Optional additional context + * @returns Structured error result + */ +export declare function createAdapterError(errorType: EditorAdapterErrorType, message: string, details?: unknown): EditorAdapterError; +/** + * Safely execute an operation that requires a specific capability. + * + * This combines capability checking with safe execution and error formatting. + * + * @param adapter - The editor adapter to use + * @param capability - The required capability + * @param operationName - Human-readable operation name + * @param operation - The async operation to execute + * @returns Result of the operation or an error + */ +export declare function executeWithCapabilityCheck(adapter: IEditorAdapter, capability: keyof IEditorAdapter['capabilities'], operationName: string, operation: () => Promise): Promise; +//# sourceMappingURL=errors.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/errors.d.ts.map b/packages/vscode-extension/dist/editors/adapters/errors.d.ts.map new file mode 100644 index 0000000..be42faa --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/errors.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/editors/adapters/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEhE;;GAEG;AACH,oBAAY,sBAAsB;IAChC,+BAA+B;IAC/B,wBAAwB,6BAA6B;IACrD,2CAA2C;IAC3C,gBAAgB,qBAAqB;IACrC,6CAA6C;IAC7C,qBAAqB,0BAA0B;IAC/C,8BAA8B;IAC9B,uBAAuB,4BAA4B;IACnD,kCAAkC;IAClC,aAAa,kBAAkB;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,OAAO,EAAE,KAAK,CAAC;IACf,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,SAAS,EAAE,sBAAsB,CAAC;IAClC,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,qBAAqB,CAAC,CAehC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,cAAc,CAAC,cAAc,CAAC,EAChD,aAAa,EAAE,MAAM,GACpB,IAAI,CAUN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,cAAc,EAChC,UAAU,EAAE,MAAM,cAAc,CAAC,cAAc,CAAC,GAC/C,IAAI,CASN;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,OAAO,GACb,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,sBAAsB,EACjC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO,GAChB,kBAAkB,CAOpB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,0BAA0B,CAAC,CAAC,EAChD,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,cAAc,CAAC,cAAc,CAAC,EAChD,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAOZ"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/errors.js b/packages/vscode-extension/dist/editors/adapters/errors.js new file mode 100644 index 0000000..4abaec0 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/errors.js @@ -0,0 +1,173 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EditorAdapterErrorType = void 0; +exports.safeExecuteCommand = safeExecuteCommand; +exports.requireCapability = requireCapability; +exports.requireMethod = requireMethod; +exports.formatAdapterError = formatAdapterError; +exports.createAdapterError = createAdapterError; +exports.executeWithCapabilityCheck = executeWithCapabilityCheck; +const vscode = __importStar(require("vscode")); +/** + * Error types for editor adapter operations. + */ +var EditorAdapterErrorType; +(function (EditorAdapterErrorType) { + /** Command execution failed */ + EditorAdapterErrorType["COMMAND_EXECUTION_FAILED"] = "COMMAND_EXECUTION_FAILED"; + /** Editor not installed or not detected */ + EditorAdapterErrorType["EDITOR_NOT_FOUND"] = "EDITOR_NOT_FOUND"; + /** Operation not supported by this editor */ + EditorAdapterErrorType["UNSUPPORTED_OPERATION"] = "UNSUPPORTED_OPERATION"; + /** Capability check failed */ + EditorAdapterErrorType["CAPABILITY_CHECK_FAILED"] = "CAPABILITY_CHECK_FAILED"; + /** Unknown or unexpected error */ + EditorAdapterErrorType["UNKNOWN_ERROR"] = "UNKNOWN_ERROR"; +})(EditorAdapterErrorType || (exports.EditorAdapterErrorType = EditorAdapterErrorType = {})); +/** + * Safe command execution wrapper that catches exceptions and returns error results. + * + * Safety: Never throws exceptions. Always returns a PromptInjectionResult with + * clear error messages if the command fails. + * + * @param command - VS Code command to execute + * @param args - Arguments to pass to the command + * @returns PromptInjectionResult indicating success or failure + */ +async function safeExecuteCommand(command, ...args) { + try { + await vscode.commands.executeCommand(command, ...args); + return { + success: true, + commandUsed: command, + }; + } + catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return { + success: false, + error: `Command '${command}' failed: ${errorMessage}`, + commandUsed: command, + }; + } +} +/** + * Check if an adapter has a specific capability before attempting an operation. + * + * Safety: Prevents invalid operations by checking capabilities first. + * Throws a clear error if the capability is not supported. + * + * @param adapter - The editor adapter to check + * @param capability - The capability key to check + * @param operationName - Human-readable name of the operation (for error messages) + * @throws Error if the capability is not supported + */ +function requireCapability(adapter, capability, operationName) { + const hasCapability = adapter.capabilities[capability]; + if (!hasCapability) { + throw new Error(`Editor ${adapter.editorName} does not support ${operationName}. ` + + `Capability '${capability}' is not available. ` + + `Sync level: ${adapter.capabilities.syncLevel}`); + } +} +/** + * Check if an adapter has implemented an optional method. + * + * Safety: Ensures that adapters claiming a capability actually implement + * the corresponding method. + * + * @param adapter - The editor adapter to check + * @param methodName - The method name to check + * @param capability - The capability that should enable this method + * @throws Error if the method is not implemented despite capability being true + */ +function requireMethod(adapter, methodName, capability) { + const method = adapter[methodName]; + if (!method || typeof method !== 'function') { + throw new Error(`Editor ${adapter.editorName} claims to support '${String(capability)}' ` + + `but does not implement ${String(methodName)}() method`); + } +} +/** + * Format an error message with context about the adapter and operation. + * + * @param adapter - The editor adapter that encountered the error + * @param operation - The operation that failed + * @param error - The error that occurred + * @returns Formatted error message with full context + */ +function formatAdapterError(adapter, operation, error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return (`Failed to ${operation} for editor ${adapter.editorName} (${adapter.editorId}): ` + + `${errorMessage}`); +} +/** + * Create a structured error result for adapter operations. + * + * @param errorType - The type of error that occurred + * @param message - Human-readable error message + * @param details - Optional additional context + * @returns Structured error result + */ +function createAdapterError(errorType, message, details) { + return { + success: false, + error: message, + errorCode: errorType, + details, + }; +} +/** + * Safely execute an operation that requires a specific capability. + * + * This combines capability checking with safe execution and error formatting. + * + * @param adapter - The editor adapter to use + * @param capability - The required capability + * @param operationName - Human-readable operation name + * @param operation - The async operation to execute + * @returns Result of the operation or an error + */ +async function executeWithCapabilityCheck(adapter, capability, operationName, operation) { + try { + requireCapability(adapter, capability, operationName); + return await operation(); + } + catch (error) { + throw new Error(formatAdapterError(adapter, operationName, error)); + } +} +//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/errors.js.map b/packages/vscode-extension/dist/editors/adapters/errors.js.map new file mode 100644 index 0000000..092a970 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/errors.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/editors/adapters/errors.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,gDAkBC;AAaD,8CAcC;AAaD,sCAaC;AAUD,gDAWC;AAUD,gDAWC;AAaD,gEAYC;AArLD,+CAAiC;AAGjC;;GAEG;AACH,IAAY,sBAWX;AAXD,WAAY,sBAAsB;IAChC,+BAA+B;IAC/B,+EAAqD,CAAA;IACrD,2CAA2C;IAC3C,+DAAqC,CAAA;IACrC,6CAA6C;IAC7C,yEAA+C,CAAA;IAC/C,8BAA8B;IAC9B,6EAAmD,CAAA;IACnD,kCAAkC;IAClC,yDAA+B,CAAA;AACjC,CAAC,EAXW,sBAAsB,sCAAtB,sBAAsB,QAWjC;AAgBD;;;;;;;;;GASG;AACI,KAAK,UAAU,kBAAkB,CACtC,OAAe,EACf,GAAG,IAAe;IAElB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACvD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,OAAO;SACrB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,YAAY,OAAO,aAAa,YAAY,EAAE;YACrD,WAAW,EAAE,OAAO;SACrB,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,iBAAiB,CAC/B,OAAuB,EACvB,UAAgD,EAChD,aAAqB;IAErB,MAAM,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IAEvD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,UAAU,OAAO,CAAC,UAAU,qBAAqB,aAAa,IAAI;YAChE,eAAe,UAAU,sBAAsB;YAC/C,eAAe,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,CAClD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,aAAa,CAC3B,OAAuB,EACvB,UAAgC,EAChC,UAAgD;IAEhD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEnC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,UAAU,OAAO,CAAC,UAAU,uBAAuB,MAAM,CAAC,UAAU,CAAC,IAAI;YACvE,0BAA0B,MAAM,CAAC,UAAU,CAAC,WAAW,CAC1D,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAChC,OAAuB,EACvB,SAAiB,EACjB,KAAc;IAEd,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE5E,OAAO,CACL,aAAa,SAAS,eAAe,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,QAAQ,KAAK;QACjF,GAAG,YAAY,EAAE,CAClB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAChC,SAAiC,EACjC,OAAe,EACf,OAAiB;IAEjB,OAAO;QACL,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,OAAO;QACd,SAAS,EAAE,SAAS;QACpB,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACI,KAAK,UAAU,0BAA0B,CAC9C,OAAuB,EACvB,UAAgD,EAChD,aAAqB,EACrB,SAA2B;IAE3B,IAAI,CAAC;QACH,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;QACtD,OAAO,MAAM,SAAS,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/types.d.ts b/packages/vscode-extension/dist/editors/adapters/types.d.ts new file mode 100644 index 0000000..053b4d0 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/types.d.ts @@ -0,0 +1,97 @@ +/** + * Capability model describing what an editor adapter can do. + */ +export interface EditorCapabilities { + /** Can inject prompts into the editor's chat panel via public commands */ + canInjectPrompt: boolean; + /** Can read chat history (only true for open-source editors like Continue) */ + canReadChatHistory: boolean; + /** Can stream assistant response tokens in real-time */ + canStreamAssistantTokens: boolean; + /** Can read diff artifacts from the editor */ + canReadDiffArtifacts: boolean; + /** Can prevent automatic application of edits */ + canPreventAutoApply: boolean; + /** Overall synchronization level: "full" | "partial" | "control-only" */ + syncLevel: 'full' | 'partial' | 'control-only'; +} +/** + * Result of a prompt injection operation. + */ +export interface PromptInjectionResult { + /** Whether the prompt injection succeeded */ + success: boolean; + /** Error message if injection failed */ + error?: string; + /** The VS Code command that was executed */ + commandUsed?: string; +} +/** + * Result of editor detection. + */ +export interface DetectionResult { + /** Whether the editor is installed and available */ + isInstalled: boolean; + /** Available commands discovered for this editor */ + availableCommands?: string[]; + /** Version information if available */ + version?: string; +} +/** + * Chat message structure for history reading. + */ +export interface ChatMessage { + /** Message role: user or assistant */ + role: 'user' | 'assistant'; + /** Message content text */ + content: string; + /** Unix timestamp when message was created */ + timestamp: number; +} +/** + * Diff artifact structure. + */ +export interface DiffArtifact { + /** Path to the file being modified */ + filePath: string; + /** Original file content before changes */ + originalContent: string; + /** Modified file content after changes */ + modifiedContent: string; + /** Whether the diff has been applied to the file */ + applied: boolean; +} +/** + * Common interface that all editor adapters must implement. + * + * Safety: All methods use only public VS Code APIs and return result objects (never throw). + */ +export interface IEditorAdapter { + /** Unique identifier for this editor (e.g., "continue", "kiro") */ + readonly editorId: string; + /** Human-readable name for this editor (e.g., "Continue", "Kiro") */ + readonly editorName: string; + /** Declared capabilities for this editor */ + readonly capabilities: EditorCapabilities; + /** + * Detect if this editor is installed and available. + * Uses vscode.commands.getCommands(true) to discover editor-specific commands. + */ + detect(): Promise; + /** + * Inject a prompt into the editor's chat panel. + * Uses vscode.commands.executeCommand with editor-specific commands. + */ + injectPrompt(prompt: string): Promise; + /** + * Read chat history if supported (optional). + * Only implemented by adapters with canReadChatHistory capability. + */ + readChatHistory?(): Promise; + /** + * Read diff artifacts if supported (optional). + * Only implemented by adapters with canReadDiffArtifacts capability. + */ + readDiffArtifacts?(): Promise; +} +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/types.d.ts.map b/packages/vscode-extension/dist/editors/adapters/types.d.ts.map new file mode 100644 index 0000000..e46b98d --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/editors/adapters/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,eAAe,EAAE,OAAO,CAAC;IAEzB,8EAA8E;IAC9E,kBAAkB,EAAE,OAAO,CAAC;IAE5B,wDAAwD;IACxD,wBAAwB,EAAE,OAAO,CAAC;IAElC,8CAA8C;IAC9C,oBAAoB,EAAE,OAAO,CAAC;IAE9B,iDAAiD;IACjD,mBAAmB,EAAE,OAAO,CAAC;IAE7B,yEAAyE;IACzE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAC;IAEjB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,WAAW,EAAE,OAAO,CAAC;IAErB,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAE3B,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAEhB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IAEjB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,CAAC;IAExB,0CAA0C;IAC1C,eAAe,EAAE,MAAM,CAAC;IAExB,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,qEAAqE;IACrE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,4CAA4C;IAC5C,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAE1C;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IAEnC;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAE7D;;;OAGG;IACH,eAAe,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAE3C;;;OAGG;IACH,iBAAiB,CAAC,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAC/C"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/types.js b/packages/vscode-extension/dist/editors/adapters/types.js new file mode 100644 index 0000000..11e638d --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/types.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/editors/adapters/types.js.map b/packages/vscode-extension/dist/editors/adapters/types.js.map new file mode 100644 index 0000000..b759148 --- /dev/null +++ b/packages/vscode-extension/dist/editors/adapters/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/editors/adapters/types.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/packages/vscode-extension/dist/extension.d.ts b/packages/vscode-extension/dist/extension.d.ts new file mode 100644 index 0000000..12b5873 --- /dev/null +++ b/packages/vscode-extension/dist/extension.d.ts @@ -0,0 +1,4 @@ +import * as vscode from 'vscode'; +export declare function activate(context: vscode.ExtensionContext): Promise; +export declare function deactivate(): void; +//# sourceMappingURL=extension.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/extension.d.ts.map b/packages/vscode-extension/dist/extension.d.ts.map new file mode 100644 index 0000000..5c19880 --- /dev/null +++ b/packages/vscode-extension/dist/extension.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAiBhC,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6G9E;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/extension.js b/packages/vscode-extension/dist/extension.js new file mode 100644 index 0000000..be86af7 --- /dev/null +++ b/packages/vscode-extension/dist/extension.js @@ -0,0 +1,151 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.activate = activate; +exports.deactivate = deactivate; +const vscode = __importStar(require("vscode")); +const KeyManager_1 = require("./auth/KeyManager"); +const LaptopIdentity_1 = require("./auth/LaptopIdentity"); +const EditorRegistry_1 = require("./editors/adapters/EditorRegistry"); +const ContinueAdapter_1 = require("./editors/adapters/ContinueAdapter"); +const KiroAdapter_1 = require("./editors/adapters/KiroAdapter"); +const CursorAdapter_1 = require("./editors/adapters/CursorAdapter"); +const AntigravityAdapter_1 = require("./editors/adapters/AntigravityAdapter"); +const SessionManager_1 = require("./auth/SessionManager"); +const WsClient_1 = require("./websocket/WsClient"); +const FileWatcher_1 = require("./diff/FileWatcher"); +const GitIntegrationModule_1 = require("./git/GitIntegrationModule"); +const SnapshotEngine_1 = require("./diff/SnapshotEngine"); +const PatchEncoder_1 = require("./diff/PatchEncoder"); +const PairingWebviewPanel_1 = require("./pairing/PairingWebviewPanel"); +const protocol_1 = require("@codelink/protocol"); +async function activate(context) { + // Auth + const keyManager = new KeyManager_1.KeyManager(context.secrets); + const laptopIdentity = new LaptopIdentity_1.LaptopIdentity(keyManager, context.globalState); + let laptopId; + try { + laptopId = await laptopIdentity.ensureRegistered(); + } + catch (err) { + vscode.window.showErrorMessage(`CodeLink: Could not register with Auth Service. ` + + `Check codelink.authServiceUrl in settings. Error: ${err}.`); + return; + } + // Editor Adapters + const registry = new EditorRegistry_1.EditorRegistry(); + registry.register(new ContinueAdapter_1.ContinueAdapter()); + registry.register(new KiroAdapter_1.KiroAdapter()); + registry.register(new CursorAdapter_1.CursorAdapter()); + registry.register(new AntigravityAdapter_1.AntigravityAdapter()); + // Session + const sessionManager = new SessionManager_1.SessionManager(keyManager, laptopId); + context.subscriptions.push({ dispose: () => sessionManager.dispose() }); + // WebSocket Client + const wsClient = new WsClient_1.WsClient({ + onConnected: () => { + vscode.window.setStatusBarMessage('$(plug) CodeLink: Mobile connected', 3000); + }, + onDisconnected: () => { + vscode.window.setStatusBarMessage('$(debug-disconnected) CodeLink: Disconnected', 3000); + }, + onMessage: async (type, payload, id) => { + switch (type) { + case 'SNAPSHOT_REQUEST': { + if (!(0, protocol_1.isSnapshotRequestPayload)(payload)) { + return; + } + const { fileName } = payload; + await fileWatcher.handleSnapshotRequest(fileName); + break; + } + case 'PATCH_ACK': + break; + case 'INJECT_PROMPT': { + if (!(0, protocol_1.isInjectPromptPayload)(payload)) { + return; + } + const { prompt } = payload; + const adapter = await registry.getBestAdapter(); + if (!adapter) { + wsClient.send('PROMPT_RESPONSE', { + originalId: id, success: false, error: 'No AI editor detected', + }); + return; + } + const result = await adapter.injectPrompt(prompt); + wsClient.send('PROMPT_RESPONSE', { + originalId: id, + success: result.success, + editorUsed: adapter.editorName, + error: result.error, + }); + break; + } + case 'SESSION_REVOKED': { + wsClient.disconnect(); + vscode.window.showInformationMessage('CodeLink: Session ended.'); + break; + } + } + } + }); + context.subscriptions.push({ dispose: () => wsClient.disconnect() }); + // Diff Engine + const git = new GitIntegrationModule_1.GitIntegrationModuleImpl(); + const snapshot = new SnapshotEngine_1.SnapshotEngine(); + const patches = new PatchEncoder_1.PatchEncoder(); + const fileWatcher = new FileWatcher_1.FileWatcher(git, snapshot, patches, wsClient); + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + if (workspaceRoot) { + fileWatcher.start(workspaceRoot); + } + context.subscriptions.push({ dispose: () => fileWatcher.stop() }); + // Commands + context.subscriptions.push(vscode.commands.registerCommand('codelink.startPairing', () => { + PairingWebviewPanel_1.PairingWebviewPanel.createOrShow(context, sessionManager, wsClient); + }), vscode.commands.registerCommand('codelink.revokeSession', async () => { + await sessionManager.revokeSession(); + wsClient.disconnect(); + vscode.window.showInformationMessage('CodeLink: Session revoked.'); + }), vscode.commands.registerCommand('codelink.showStatus', () => { + const state = sessionManager.state; + const sess = sessionManager.session; + vscode.window.showInformationMessage(`CodeLink status ${state}` + (sess ? ` | session: ${sess.sessionId.slice(0, 12)}...` : '')); + })); +} +function deactivate() { +} +//# sourceMappingURL=extension.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/extension.js.map b/packages/vscode-extension/dist/extension.js.map new file mode 100644 index 0000000..b2e1d33 --- /dev/null +++ b/packages/vscode-extension/dist/extension.js.map @@ -0,0 +1 @@ +{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,4BA6GC;AAED,gCAEC;AAlID,+CAAgC;AAChC,kDAA+C;AAC/C,0DAAuD;AACvD,sEAAmE;AACnE,wEAAqE;AACrE,gEAA6D;AAC7D,oEAAiE;AACjE,8EAA2E;AAC3E,0DAAuD;AACvD,mDAAgD;AAChD,oDAAiD;AACjD,qEAAsE;AACtE,0DAAuD;AACvD,sDAAmD;AACnD,uEAAoE;AACpE,iDAAoF;AAE7E,KAAK,UAAU,QAAQ,CAAC,OAAgC;IAE3D,OAAO;IACP,MAAM,UAAU,GAAG,IAAI,uBAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,cAAc,GAAG,IAAI,+BAAc,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAE3E,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACD,QAAQ,GAAG,MAAM,cAAc,CAAC,gBAAgB,EAAE,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAC1B,kDAAkD;YAClD,qDAAqD,GAAG,GAAG,CAC9D,CAAC;QACF,OAAO;IACX,CAAC;IAED,kBAAkB;IAClB,MAAM,QAAQ,GAAG,IAAI,+BAAc,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,IAAI,iCAAe,EAAE,CAAC,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,IAAI,yBAAW,EAAE,CAAC,CAAC;IACrC,QAAQ,CAAC,QAAQ,CAAC,IAAI,6BAAa,EAAE,CAAC,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,IAAI,uCAAkB,EAAE,CAAC,CAAC;IAE5C,UAAU;IACV,MAAM,cAAc,GAAG,IAAI,+BAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAChE,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAExE,mBAAmB;IACnB,MAAM,QAAQ,GAAG,IAAI,mBAAQ,CAAC;QAC1B,WAAW,EAAE,GAAG,EAAE;YACd,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,oCAAoC,EAAE,IAAK,CAAC,CAAC;QACnF,CAAC;QACD,cAAc,EAAE,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,8CAA8C,EAAE,IAAK,CAAC,CAAC;QAC7F,CAAC;QACD,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE;YACnC,QAAQ,IAAI,EAAE,CAAC;gBACX,KAAK,kBAAkB,CAAC,CAAC,CAAC;oBACtB,IAAI,CAAC,IAAA,mCAAwB,EAAC,OAAO,CAAC,EAAE,CAAC;wBAAC,OAAO;oBAAC,CAAC;oBACnD,MAAM,EAAE,QAAQ,EAAE,GAAG,OAA+B,CAAC;oBACrD,MAAM,WAAW,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;oBAClD,MAAM;gBACV,CAAC;gBACD,KAAK,WAAW;oBACZ,MAAM;gBACV,KAAK,eAAe,CAAC,CAAC,CAAC;oBACnB,IAAI,CAAC,IAAA,gCAAqB,EAAC,OAAO,CAAC,EAAE,CAAC;wBAAC,OAAO;oBAAC,CAAC;oBAChD,MAAM,EAAE,MAAM,EAAE,GAAG,OAA6B,CAAC;oBACjD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,cAAc,EAAE,CAAC;oBAChD,IAAI,CAAC,OAAO,EAAE,CAAC;wBACX,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE;4BAC7B,UAAU,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB;yBACjE,CAAC,CAAC;wBACH,OAAO;oBACX,CAAC;oBACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;oBAClD,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE;wBAC7B,UAAU,EAAE,EAAE;wBACd,OAAO,EAAE,MAAM,CAAC,OAAO;wBACvB,UAAU,EAAE,OAAO,CAAC,UAAU;wBAC9B,KAAK,EAAE,MAAM,CAAC,KAAK;qBACtB,CAAC,CAAC;oBACH,MAAM;gBACV,CAAC;gBACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;oBACrB,QAAQ,CAAC,UAAU,EAAE,CAAC;oBACtB,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,0BAA0B,CAAC,CAAC;oBACjE,MAAM;gBACV,CAAC;YACL,CAAC;QACL,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAErE,cAAc;IACd,MAAM,GAAG,GAAG,IAAI,+CAAwB,EAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,+BAAc,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,2BAAY,EAAE,CAAC;IACnC,MAAM,WAAW,GAAG,IAAI,yBAAW,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAEtE,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC;IACzE,IAAI,aAAa,EAAE,CAAC;QAChB,WAAW,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAElE,WAAW;IACX,OAAO,CAAC,aAAa,CAAC,IAAI,CACtB,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC1D,yCAAmB,CAAC,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;IACxE,CAAC,CAAC,EAEF,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACrC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,4BAA4B,CAAC,CAAC;IACvE,CAAC,CAAC,EAEF,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACxD,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC;QACnC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAChC,mBAAmB,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAC7F,CAAC;IACN,CAAC,CAAC,CACL,CAAC;AACN,CAAC;AAED,SAAgB,UAAU;AAE1B,CAAC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/git/GitIntegrationModule.d.ts b/packages/vscode-extension/dist/git/GitIntegrationModule.d.ts new file mode 100644 index 0000000..d72d654 --- /dev/null +++ b/packages/vscode-extension/dist/git/GitIntegrationModule.d.ts @@ -0,0 +1,41 @@ +/** + * Git Integration Module + * Handles Git operations for fetching HEAD versions of files + */ +export interface GitIntegrationModule { + /** + * Initialize and locate the Git repository + * @param workspaceRoot - The workspace root directory + * @returns true if Git repository found, false otherwise + */ + initialize(workspaceRoot: string): Promise; + /** + * Fetch the HEAD version of a file + * Returns empty string if file is untracked or Git operation fails + * @param filePath - Absolute path to the file + * @returns Content from HEAD or empty string + */ + getHeadVersion(filePath: string): Promise; + /** + * Check if a file is tracked by Git + * @param filePath - Absolute path to the file + * @returns true if tracked, false otherwise + */ + isTracked(filePath: string): Promise; +} +export declare class GitIntegrationModuleImpl implements GitIntegrationModule { + private git; + private workspaceRoot; + private repositoryRoot; + private isInitialized; + initialize(workspaceRoot: string): Promise; + getHeadVersion(filePath: string): Promise; + isTracked(filePath: string): Promise; + /** + * Convert absolute file path to repository-relative path + * @param filePath - Absolute path to the file + * @returns Repository-relative path + */ + private getRelativePath; +} +//# sourceMappingURL=GitIntegrationModule.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/git/GitIntegrationModule.d.ts.map b/packages/vscode-extension/dist/git/GitIntegrationModule.d.ts.map new file mode 100644 index 0000000..e2f42e5 --- /dev/null +++ b/packages/vscode-extension/dist/git/GitIntegrationModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GitIntegrationModule.d.ts","sourceRoot":"","sources":["../../src/git/GitIntegrationModule.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpD;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/C;AAED,qBAAa,wBAAyB,YAAW,oBAAoB;IACnE,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,aAAa,CAAkB;IAEjC,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBnD,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqCjD,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBnD;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAOxB"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/git/GitIntegrationModule.js b/packages/vscode-extension/dist/git/GitIntegrationModule.js new file mode 100644 index 0000000..61613be --- /dev/null +++ b/packages/vscode-extension/dist/git/GitIntegrationModule.js @@ -0,0 +1,120 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GitIntegrationModuleImpl = void 0; +const simple_git_1 = __importDefault(require("simple-git")); +const vscode = __importStar(require("vscode")); +class GitIntegrationModuleImpl { + constructor() { + this.git = null; + this.workspaceRoot = ''; + this.repositoryRoot = ''; + this.isInitialized = false; + } + async initialize(workspaceRoot) { + try { + this.workspaceRoot = workspaceRoot; + this.git = (0, simple_git_1.default)(workspaceRoot); + // Find the repository root + const result = await this.git.revparse(['--show-toplevel']); + this.repositoryRoot = result.trim(); + this.isInitialized = true; + console.log(`[GitIntegration] Initialized successfully at ${this.repositoryRoot}`); + return true; + } + catch (error) { + console.warn('[GitIntegration] Git repository not found:', error); + this.isInitialized = false; + return false; + } + } + async getHeadVersion(filePath) { + if (!this.isInitialized || !this.git) { + console.warn('[GitIntegration] Not initialized, returning empty HEAD content'); + return ''; + } + const startTime = Date.now(); + try { + // Convert absolute path to repository-relative path + const relativePath = this.getRelativePath(filePath); + // Fetch HEAD version using git show + const content = await this.git.show([`HEAD:${relativePath}`]); + const elapsed = Date.now() - startTime; + console.log(`[GitIntegration] Fetched HEAD version for ${relativePath} (${content.length} bytes, took ${elapsed}ms)`); + // Performance warning if Git operation took too long + if (elapsed > 500) { + console.warn(`[GitIntegration] Git operation exceeded 500ms threshold: ${elapsed}ms for ${relativePath}`); + } + return content; + } + catch (error) { + const elapsed = Date.now() - startTime; + // File is likely untracked or not in HEAD + // Return empty string to indicate new/untracked file + console.debug(`[GitIntegration] File not in HEAD (${filePath}, took ${elapsed}ms): ${error}`); + return ''; + } + } + async isTracked(filePath) { + if (!this.isInitialized || !this.git) { + return false; + } + try { + const relativePath = this.getRelativePath(filePath); + // Check if file exists in HEAD + await this.git.show([`HEAD:${relativePath}`]); + return true; + } + catch (error) { + return false; + } + } + /** + * Convert absolute file path to repository-relative path + * @param filePath - Absolute path to the file + * @returns Repository-relative path + */ + getRelativePath(filePath) { + // Use VS Code's workspace API to get relative path + const relativePath = vscode.workspace.asRelativePath(filePath, false); + // Normalize path separators for Git (always use forward slashes) + return relativePath.replace(/\\/g, '/'); + } +} +exports.GitIntegrationModuleImpl = GitIntegrationModuleImpl; +//# sourceMappingURL=GitIntegrationModule.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/git/GitIntegrationModule.js.map b/packages/vscode-extension/dist/git/GitIntegrationModule.js.map new file mode 100644 index 0000000..4159d67 --- /dev/null +++ b/packages/vscode-extension/dist/git/GitIntegrationModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GitIntegrationModule.js","sourceRoot":"","sources":["../../src/git/GitIntegrationModule.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4DAAkD;AAClD,+CAAiC;AA8BjC,MAAa,wBAAwB;IAArC;QACU,QAAG,GAAqB,IAAI,CAAC;QAC7B,kBAAa,GAAW,EAAE,CAAC;QAC3B,mBAAc,GAAW,EAAE,CAAC;QAC5B,kBAAa,GAAY,KAAK,CAAC;IAsFzC,CAAC;IApFC,KAAK,CAAC,UAAU,CAAC,aAAqB;QACpC,IAAI,CAAC;YACH,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;YACnC,IAAI,CAAC,GAAG,GAAG,IAAA,oBAAS,EAAC,aAAa,CAAC,CAAC;YAEpC,2BAA2B;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAC5D,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;YACpC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAE1B,OAAO,CAAC,GAAG,CAAC,gDAAgD,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;YACnF,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,4CAA4C,EAAE,KAAK,CAAC,CAAC;YAClE,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,QAAgB;QACnC,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;YAC/E,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,IAAI,CAAC;YACH,oDAAoD;YACpD,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YAEpD,oCAAoC;YACpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,YAAY,EAAE,CAAC,CAAC,CAAC;YAE9D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACvC,OAAO,CAAC,GAAG,CACT,6CAA6C,YAAY,KAAK,OAAO,CAAC,MAAM,gBAAgB,OAAO,KAAK,CACzG,CAAC;YAEF,qDAAqD;YACrD,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CACV,4DAA4D,OAAO,UAAU,YAAY,EAAE,CAC5F,CAAC;YACJ,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACvC,0CAA0C;YAC1C,qDAAqD;YACrD,OAAO,CAAC,KAAK,CAAC,sCAAsC,QAAQ,UAAU,OAAO,QAAQ,KAAK,EAAE,CAAC,CAAC;YAC9F,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YAEpD,+BAA+B;YAC/B,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,YAAY,EAAE,CAAC,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,eAAe,CAAC,QAAgB;QACtC,mDAAmD;QACnD,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAEtE,iEAAiE;QACjE,OAAO,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;CACF;AA1FD,4DA0FC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/pairing/PairingWebviewPanel.d.ts b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.d.ts new file mode 100644 index 0000000..f0f8b29 --- /dev/null +++ b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.d.ts @@ -0,0 +1,31 @@ +import * as vscode from 'vscode'; +import { SessionManager } from '../auth/SessionManager'; +import { WsClient } from '../websocket/WsClient'; +/** + * VS Code Webview panel that displays the QR code for mobile pairing. + * + * Lifecycle: + * 1. Extension calls createOrShow() + * 2. Panel creates a session on the Auth Service + * 3. Renders QR code with 60-second countdown + * 4. Polls Auth Service every 2s via SessionManager.waitForMobile() + * 5. On success: connects WsClient, shows "Paired βœ“", offers Revoke button + * 6. On timeout: auto-refreshes (new QR) + */ +export declare class PairingWebviewPanel { + private readonly context; + private readonly sessionManager; + private readonly wsClient; + private static instance; + private readonly panel; + private disposed; + static createOrShow(context: vscode.ExtensionContext, sessionManager: SessionManager, wsClient: WsClient): void; + private constructor(); + private startPairingFlow; + private loadingHtml; + private pairingHtml; + private pairedHtml; + private errorHtml; + private wrap; +} +//# sourceMappingURL=PairingWebviewPanel.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/pairing/PairingWebviewPanel.d.ts.map b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.d.ts.map new file mode 100644 index 0000000..8feec2f --- /dev/null +++ b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"PairingWebviewPanel.d.ts","sourceRoot":"","sources":["../../src/pairing/PairingWebviewPanel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD;;;;;;;;;;GAUG;AAEH,qBAAa,mBAAmB;IA8BxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IA/B7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAkC;IAEzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAS;IAEzB,MAAM,CAAC,YAAY,CACf,OAAO,EAAE,MAAM,CAAC,gBAAgB,EAChC,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,QAAQ,GACnB,IAAI;IAkBP,OAAO;YAwBO,gBAAgB;IAiChC,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,WAAW;IAwBnB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,IAAI;CAoBb"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/pairing/PairingWebviewPanel.js b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.js new file mode 100644 index 0000000..45c96b2 --- /dev/null +++ b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.js @@ -0,0 +1,182 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PairingWebviewPanel = void 0; +const vscode = __importStar(require("vscode")); +const qrcode = __importStar(require("qrcode")); +/** + * VS Code Webview panel that displays the QR code for mobile pairing. + * + * Lifecycle: + * 1. Extension calls createOrShow() + * 2. Panel creates a session on the Auth Service + * 3. Renders QR code with 60-second countdown + * 4. Polls Auth Service every 2s via SessionManager.waitForMobile() + * 5. On success: connects WsClient, shows "Paired βœ“", offers Revoke button + * 6. On timeout: auto-refreshes (new QR) + */ +class PairingWebviewPanel { + static createOrShow(context, sessionManager, wsClient) { + if (PairingWebviewPanel.instance) { + PairingWebviewPanel.instance.panel.reveal(); + return; + } + const panel = vscode.window.createWebviewPanel('codelinkPairing', 'CodeLink - Pair Mobile', vscode.ViewColumn.Beside, { enableScripts: true, retainContextWhenHidden: true }); + PairingWebviewPanel.instance = new PairingWebviewPanel(panel, context, sessionManager, wsClient); + } + constructor(panel, context, sessionManager, wsClient) { + this.context = context; + this.sessionManager = sessionManager; + this.wsClient = wsClient; + this.disposed = false; + this.panel = panel; + panel.onDidDispose(() => { + this.disposed = true; + PairingWebviewPanel.instance = undefined; + }, null, context.subscriptions); + panel.webview.onDidReceiveMessage(msg => { + if (msg.command === 'revoke') { + this.sessionManager.revokeSession(); + } + else if (msg.command === 'refresh') { + this.startPairingFlow(); + } + }); + this.startPairingFlow(); + } + async startPairingFlow() { + if (this.disposed) { + return; + } + try { + this.panel.webview.html = this.loadingHtml('Creating Session...'); + const { sessionId, qrPayload, expiresAt } = await this.sessionManager.createSession(); + const qrDataUrl = await qrcode.toDataURL(qrPayload, { width: 280, margin: 2 }); + if (this.disposed) { + return; + } + this.panel.webview.html = this.pairingHtml(qrDataUrl, expiresAt, qrPayload); + const session = await this.sessionManager.waitForMobile(sessionId, 90000); + if (this.disposed) { + return; + } + this.wsClient.connect(session.relayWssUrl, session.laptopToken); + this.panel.webview.html = this.pairedHtml(session.sessionId); + } + catch (err) { + if (this.disposed) { + return; + } + const msg = err instanceof Error ? err.message : String(err); + if (msg.includes('timed out')) { + this.startPairingFlow(); + } + else { + this.panel.webview.html = this.errorHtml(msg); + } + } + } + // HTML Templates + loadingHtml(message) { + return this.wrap(`

${message}

`); + } + pairingHtml(qrDataUrl, expiresAt, qrPayload) { + const secondsLeft = Math.max(0, Math.round((expiresAt - Date.now()) / 1000)); + return this.wrap(` +

Scan with your phone

+ QR code +

Code expires in ${secondsLeft}s

+ + + + `); + } + pairedHtml(sessionId) { + return this.wrap(` +

βœ“ Mobile paired

+

Session: ${sessionId.slice(0, 16)}…

+ + + `); + } + errorHtml(message) { + return this.wrap(` +

⚠ Error

+

${message}

+ + + `); + } + wrap(body) { + return ` + + + + + +${body} +`; + } +} +exports.PairingWebviewPanel = PairingWebviewPanel; +//# sourceMappingURL=PairingWebviewPanel.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/pairing/PairingWebviewPanel.js.map b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.js.map new file mode 100644 index 0000000..9a0a843 --- /dev/null +++ b/packages/vscode-extension/dist/pairing/PairingWebviewPanel.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PairingWebviewPanel.js","sourceRoot":"","sources":["../../src/pairing/PairingWebviewPanel.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,+CAAiC;AAIjC;;;;;;;;;;GAUG;AAEH,MAAa,mBAAmB;IAM5B,MAAM,CAAC,YAAY,CACf,OAAgC,EAChC,cAA8B,EAC9B,QAAkB;QAElB,IAAI,mBAAmB,CAAC,QAAQ,EAAE,CAAC;YAC/B,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAC5C,OAAO;QACX,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAC1C,iBAAiB,EACjB,wBAAwB,EACxB,MAAM,CAAC,UAAU,CAAC,MAAM,EACxB,EAAE,aAAa,EAAE,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,CACzD,CAAC;QAEF,mBAAmB,CAAC,QAAQ,GAAG,IAAI,mBAAmB,CAClD,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,CAC3C,CAAC;IACN,CAAC;IAED,YACI,KAA0B,EACT,OAAgC,EAChC,cAA8B,EAC9B,QAAkB;QAFlB,YAAO,GAAP,OAAO,CAAyB;QAChC,mBAAc,GAAd,cAAc,CAAgB;QAC9B,aAAQ,GAAR,QAAQ,CAAU;QA5B/B,aAAQ,GAAG,KAAK,CAAC;QA8BrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE;YACpB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,mBAAmB,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC7C,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;QAEhC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE;YACpC,IAAI,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC3B,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC;YACxC,CAAC;iBAAM,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBACnC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC5B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC1B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAE9B,IAAI,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC;YAElE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC;YAEtF,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAE/E,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YAE5E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,SAAS,EAAE,KAAM,CAAC,CAAC;YAE3E,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YAE9B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;YAChE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEjE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YAC9B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAE7D,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;IACL,CAAC;IAED,iBAAiB;IACX,WAAW,CAAC,OAAe;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,OAAO,MAAM,CAAC,CAAC;IACtD,CAAC;IAEO,WAAW,CAAC,SAAiB,EAAE,SAAiB,EAAE,SAAiB;QACzE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,IAAI,CAAC;;kBAEH,SAAS;8DACmC,WAAW;;;iBAGxD,SAAS;;;kBAGR,WAAW;;;;;;;;;;KAUxB,CAAC,CAAC;IACL,CAAC;IAEO,UAAU,CAAC,SAAiB;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC;;kCAEa,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;;;;;;KAMnD,CAAC,CAAC;IACL,CAAC;IAEO,SAAS,CAAC,OAAe;QAC/B,OAAO,IAAI,CAAC,IAAI,CAAC;;yBAEI,OAAO;;;;;;KAM3B,CAAC,CAAC;IACL,CAAC;IAEO,IAAI,CAAC,IAAY;QACvB,OAAO;;;;;;;;;;;;;;;;QAgBH,IAAI;QACJ,CAAC;IACP,CAAC;CACF;AA7JD,kDA6JC"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/websocket/WsClient.d.ts b/packages/vscode-extension/dist/websocket/WsClient.d.ts new file mode 100644 index 0000000..2618d1b --- /dev/null +++ b/packages/vscode-extension/dist/websocket/WsClient.d.ts @@ -0,0 +1,29 @@ +import { MessageType, PayloadFor } from '@codelink/protocol'; +interface WsClientOptions { + onMessage: (type: string, payload: unknown, id: string) => void; + onConnected: () => void; + onDisconnected: () => void; +} +export declare class WsClient { + private readonly opts; + private ws; + private token; + private url; + private reconnectDelay; + private readonly MAX_DELAY; + private reconnectTimer; + private sendQueue; + private outSeq; + private connected; + private intentionalClose; + constructor(opts: WsClientOptions); + connect(url: string, token: string): void; + private _connect; + send(type: T, payload: PayloadFor): void; + isConnected(): boolean; + disconnect(): void; + private flushQueue; + private scheduleReconnect; +} +export {}; +//# sourceMappingURL=WsClient.d.ts.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/websocket/WsClient.d.ts.map b/packages/vscode-extension/dist/websocket/WsClient.d.ts.map new file mode 100644 index 0000000..0aa5499 --- /dev/null +++ b/packages/vscode-extension/dist/websocket/WsClient.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"WsClient.d.ts","sourceRoot":"","sources":["../../src/websocket/WsClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,WAAW,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAE3E,UAAU,eAAe;IACvB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,qBAAa,QAAQ;IAYL,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAS;gBAEJ,IAAI,EAAE,eAAe;IAElD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAOzC,OAAO,CAAC,QAAQ;IAyChB,IAAI,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAUlE,WAAW,IAAI,OAAO;IAItB,UAAU,IAAI,IAAI;IAWlB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,iBAAiB;CAO5B"} \ No newline at end of file diff --git a/packages/vscode-extension/dist/websocket/WsClient.js b/packages/vscode-extension/dist/websocket/WsClient.js new file mode 100644 index 0000000..63e6ad2 --- /dev/null +++ b/packages/vscode-extension/dist/websocket/WsClient.js @@ -0,0 +1,99 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WsClient = void 0; +const ws_1 = __importDefault(require("ws")); +const protocol_1 = require("@codelink/protocol"); +class WsClient { + constructor(opts) { + this.opts = opts; + this.ws = null; + this.token = null; + this.url = null; + this.reconnectDelay = 1000; + this.MAX_DELAY = 30000; + this.reconnectTimer = null; + this.sendQueue = []; + this.outSeq = 0; + this.connected = false; + this.intentionalClose = false; + } + connect(url, token) { + this.url = url; + this.token = token; + this.intentionalClose = false; + this._connect(); + } + _connect() { + if (!this.url || !this.token) { + return; + } + const wsUrl = `${this.url}?token=${encodeURIComponent(this.token)}`; + this.ws = new ws_1.default(wsUrl, { + perMessageDeflate: true, + }); + this.ws.on('open', () => { + this.connected = true; + this.reconnectDelay = 1000; + this.opts.onConnected(); + this.flushQueue(); + }); + this.ws.on('message', (data) => { + try { + const envelope = JSON.parse(data.toString('utf8')); + this.opts.onMessage(envelope.type, envelope.payload, envelope.id); + } + catch { + // ignore parsing failures + } + }); + this.ws.on('close', () => { + this.connected = false; + this.opts.onDisconnected(); + if (!this.intentionalClose) { + this.scheduleReconnect(); + } + }); + this.ws.on('error', () => { + this.ws?.terminate(); + }); + } + send(type, payload) { + const envelope = JSON.stringify((0, protocol_1.buildEnvelope)(type, payload)); + if (this.ws?.readyState === ws_1.default.OPEN) { + this.ws.send(envelope); + } + else { + this.sendQueue.push(envelope); + } + } + isConnected() { + return this.connected; + } + disconnect() { + this.intentionalClose = true; + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + this.ws?.close(1000, 'extension deactivated'); + this.ws = null; + this.connected = false; + } + flushQueue() { + while (this.sendQueue.length > 0 && this.ws?.readyState === ws_1.default.OPEN) { + this.ws.send(this.sendQueue.shift()); + } + } + scheduleReconnect() { + const jitter = Math.random() * 500; + this.reconnectTimer = setTimeout(() => { + this._connect(); + }, this.reconnectDelay + jitter); + this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.MAX_DELAY); + } +} +exports.WsClient = WsClient; +//# sourceMappingURL=WsClient.js.map \ No newline at end of file diff --git a/packages/vscode-extension/dist/websocket/WsClient.js.map b/packages/vscode-extension/dist/websocket/WsClient.js.map new file mode 100644 index 0000000..956dbe9 --- /dev/null +++ b/packages/vscode-extension/dist/websocket/WsClient.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WsClient.js","sourceRoot":"","sources":["../../src/websocket/WsClient.ts"],"names":[],"mappings":";;;;;;AAAA,4CAA2B;AAC3B,iDAA2E;AAQ3E,MAAa,QAAQ;IAYjB,YAA6B,IAAqB;QAArB,SAAI,GAAJ,IAAI,CAAiB;QAX1C,OAAE,GAAqB,IAAI,CAAC;QAC5B,UAAK,GAAkB,IAAI,CAAC;QAC5B,QAAG,GAAkB,IAAI,CAAC;QAC1B,mBAAc,GAAG,IAAK,CAAC;QACd,cAAS,GAAG,KAAM,CAAC;QAC5B,mBAAc,GAA0B,IAAI,CAAC;QAC7C,cAAS,GAAa,EAAE,CAAC;QACzB,WAAM,GAAG,CAAC,CAAC;QACX,cAAS,GAAG,KAAK,CAAC;QAClB,qBAAgB,GAAG,KAAK,CAAC;IAEoB,CAAC;IAEtD,OAAO,CAAC,GAAW,EAAE,KAAa;QAC9B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpB,CAAC;IAEO,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;QAEzC,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,GAAG,UAAU,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC,EAAE,GAAG,IAAI,YAAS,CAAC,KAAK,EAAE;YAC3B,iBAAiB,EAAE,IAAI;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,cAAc,GAAG,IAAK,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACxB,IAAI,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE;YACnC,IAAI,CAAC;gBACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAIhD,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YACtE,CAAC;YAAC,MAAM,CAAC;gBACL,0BAA0B;YAC9B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC7B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC;QACzB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,IAAI,CAAwB,IAAO,EAAE,OAAsB;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAA,wBAAa,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAE9D,IAAI,IAAI,CAAC,EAAE,EAAE,UAAU,KAAK,YAAS,CAAC,IAAI,EAAE,CAAC;YACzC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC;IACL,CAAC;IAED,WAAW;QACP,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,UAAU;QACN,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC/B,CAAC;QACD,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;QAC9C,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IAC3B,CAAC;IAEO,UAAU;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,UAAU,KAAK,YAAS,CAAC,IAAI,EAAE,CAAC;YACzE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAG,CAAC,CAAC;QAC1C,CAAC;IACL,CAAC;IAEO,iBAAiB;QACrB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC;QACnC,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpB,CAAC,EAAE,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5E,CAAC;CACJ;AApGD,4BAoGC"} \ No newline at end of file diff --git a/packages/vscode-extension/package.json b/packages/vscode-extension/package.json index 19a9710..710981d 100644 --- a/packages/vscode-extension/package.json +++ b/packages/vscode-extension/package.json @@ -1,7 +1,7 @@ { "name": "codelink-extension", "displayName": "CodeLink", - "version": "0.1.0", + "version": "2.0.0", "publisher": "codelink", "engines": { "vscode": "^1.80.0" @@ -11,23 +11,36 @@ ], "main": "./dist/extension.js", "activationEvents": [ - "*" + "onStartupFinished" ], "contributes": { "commands": [ { - "command": "codelink.hello", - "title": "CodeLink: Hello World" + "command": "codelink.startPairing", + "title": "CodeLink: Pair Mobile Device" + }, + { + "command": "codelink.revokeSession", + "title": "CodeLink: Revoke Mobile Session" + }, + { + "command": "codelink.showStatus", + "title": "CodeLink: Show Connection Status" } - ] - }, - "configuration": { - "title": "CodeLink", - "properties": { - "codelink.relayServerUrl": { - "type": "string", - "default": "http://localhost:8080", - "description": "URL of the CodeLink Relay Server" + ], + "configuration": { + "title": "CodeLink", + "properties": { + "codelink.authServiceUrl": { + "type": "string", + "default": "http://localhost:8081", + "description": "URL of the CodeLink Auth Service" + }, + "codelink.relayServiceUrl": { + "type": "string", + "default": "ws://localhost:8082", + "description": "WebSocket URL of the CodeLink Relay Service" + } } } }, @@ -37,13 +50,18 @@ "test": "vitest run" }, "dependencies": { - "@codelink/protocol": "*", + "@codelink/protocol": "^2.0.0", + "diff-match-patch": "^1.0.5", + "qrcode": "^1.5.4", "simple-git": "^3.25.0", - "socket.io-client": "^4.8.3" + "ws": "^8.18.0" }, "devDependencies": { - "@types/node": "^25.3.0", + "@types/diff-match-patch": "^1.0.36", + "@types/node": "^20.0.0", + "@types/qrcode": "^1.5.5", "@types/vscode": "^1.80.0", + "@types/ws": "^8.5.13", "typescript": "^5.0.0", "vitest": "^4.0.18" } diff --git a/packages/vscode-extension/src/auth/KeyManager.ts b/packages/vscode-extension/src/auth/KeyManager.ts new file mode 100644 index 0000000..1b1ac97 --- /dev/null +++ b/packages/vscode-extension/src/auth/KeyManager.ts @@ -0,0 +1,42 @@ +import * as vscode from 'vscode'; +import { createSign, generateKeyPairSync } from 'crypto'; + +const PRIVATE_KEY_SECRET = 'codelink.rsa.privateKey'; +const PUBLIC_KEY_SECRET = 'codelink.rsa.publicKey'; + +export class KeyManager { + constructor(private readonly secrets: vscode.SecretStorage) {} + + async getOrCreateKeyPair(): Promise<{ privateKeyPem: string; publicKeyPem: string }> { + let privateKeyPem = await this.secrets.get(PRIVATE_KEY_SECRET); + let publicKeyPem = await this.secrets.get(PUBLIC_KEY_SECRET); + + if (!privateKeyPem || !publicKeyPem) { + const { privateKey, publicKey } = generateKeyPairSync('rsa', { + modulusLength: 2048, + publicKeyEncoding: { type: 'spki', format: 'pem' }, + privateKeyEncoding: { type: 'pkcs8', format: 'pem' }, + }); + + privateKeyPem = privateKey; + publicKeyPem = publicKey; + + await this.secrets.store(PRIVATE_KEY_SECRET, privateKeyPem); + await this.secrets.store(PUBLIC_KEY_SECRET, publicKeyPem); + } + + return { privateKeyPem, publicKeyPem }; + } + + async getPublicKeyPem(): Promise { + const { publicKeyPem } = await this.getOrCreateKeyPair(); + return publicKeyPem; + } + + async signRequest(body: string): Promise { + const { privateKeyPem } = await this.getOrCreateKeyPair(); + const signer = createSign('SHA256'); + signer.update(body); + return signer.sign(privateKeyPem, 'base64'); + } +} diff --git a/packages/vscode-extension/src/auth/LaptopIdentity.ts b/packages/vscode-extension/src/auth/LaptopIdentity.ts new file mode 100644 index 0000000..045d693 --- /dev/null +++ b/packages/vscode-extension/src/auth/LaptopIdentity.ts @@ -0,0 +1,48 @@ +import * as vscode from 'vscode'; +import { KeyManager } from './KeyManager'; + +const LAPTOP_ID_KEY = 'codelink.laptopId'; + +export class LaptopIdentity { + constructor( + private readonly keyManager: KeyManager, + private readonly globalState: vscode.Memento + ) {} + + async ensureRegistered(): Promise { + const cached = this.globalState.get(LAPTOP_ID_KEY); + if (cached) { + return cached; + } + return this.register(); + } + + private async register(): Promise { + const authUrl = this.getAuthUrl(); + const publicKeyPem = await this.keyManager.getPublicKeyPem(); + + const response = await fetch(`${authUrl}/v1/laptops/register`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ publicKeyPem }), + }); + + if (!response.ok) { + throw new Error(`Laptop registration failed: ${response.status} ${response.statusText}`); + } + + const { laptopId } = (await response.json()) as { laptopId: string }; + await this.globalState.update(LAPTOP_ID_KEY, laptopId); + return laptopId; + } + + getLaptopId(): string | undefined { + return this.globalState.get(LAPTOP_ID_KEY); + } + + private getAuthUrl(): string { + return vscode.workspace + .getConfiguration('codelink') + .get('authServiceUrl', 'http://localhost:8081'); + } +} diff --git a/packages/vscode-extension/src/auth/SessionManager.ts b/packages/vscode-extension/src/auth/SessionManager.ts new file mode 100644 index 0000000..bd8ceaa --- /dev/null +++ b/packages/vscode-extension/src/auth/SessionManager.ts @@ -0,0 +1,152 @@ +import * as vscode from 'vscode'; +import { KeyManager } from './KeyManager'; + +export type SessionState = 'idle' | 'pending' | 'active' | 'revoked'; + +export interface ActiveSession { + sessionId: string; + laptopToken: string; + relayWssUrl: string; +} + +export class SessionManager { + private _state: SessionState = 'idle'; + private _session: ActiveSession | null = null; + private _pollTimer: NodeJS.Timeout | null = null; + + private readonly _onStateChange = new vscode.EventEmitter(); + readonly onStateChange = this._onStateChange.event; + + constructor( + private readonly keyManager: KeyManager, + private readonly laptopId: string + ) {} + + get state(): SessionState { + return this._state; + } + get session(): ActiveSession | null { + return this._session; + } + + async createSession(): Promise<{ sessionId: string; qrPayload: string; expiresAt: number }> { + const authUrl = this.getAuthUrl(); + const requestedAt = Date.now(); + const body = JSON.stringify({ laptopId: this.laptopId, requestedAt }); + const sig = await this.keyManager.signRequest(body); + + const response = await fetch(`${authUrl}/v1/sessions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Laptop-Id': this.laptopId, + 'X-Laptop-Sig': sig, + }, + body, + }); + + if (!response.ok) { + throw new Error(`Session creation failed: ${response.status}`); + } + + const data = (await response.json()) as { + sessionId: string; + qrPayload: string; + expiresAt: number; + }; + + this._state = 'pending'; + this._onStateChange.fire('pending'); + return data; + } + + async waitForMobile(sessionId: string, timeoutMs = 90_000): Promise { + const authUrl = this.getAuthUrl(); + const deadline = Date.now() + timeoutMs; + + return new Promise((resolve, reject) => { + const poll = async () => { + if (Date.now() > deadline) { + this._setState('idle'); + return reject(new Error('Pairing timed out - QR code expired')); + } + + try { + const sig = await this.keyManager.signRequest(''); + const response = await fetch(`${authUrl}/v1/sessions/${sessionId}/status`, { + headers: { + 'X-Laptop-Id': this.laptopId, + 'X-Laptop-Sig': sig, + }, + }); + + if (!response.ok) { + return reject(new Error(`Status poll failed: ${response.status}`)); + } + + const data = (await response.json()) as { + state: string; + laptopToken: string | null; + }; + + if (data.state === 'active' && data.laptopToken) { + const relayBase = vscode.workspace + .getConfiguration('codelink') + .get('relayServiceUrl', 'ws://localhost:8082'); + + const session: ActiveSession = { + sessionId, + laptopToken: data.laptopToken, + relayWssUrl: `${relayBase}/ws`, + }; + this._session = session; + this._setState('active'); + return resolve(session); + } + } catch (err) { + reject(err); + } + }; + poll(); + }); + } + + async revokeSession(): Promise { + if (!this._session) { + return; + } + + const authUrl = this.getAuthUrl(); + const sig = await this.keyManager.signRequest(''); + + await fetch(`${authUrl}/v1/sessions/${this._session.sessionId}`, { + method: 'DELETE', + headers: { + 'X-Laptop-Id': this.laptopId, + 'X-Laptop-Sig': sig, + }, + }); + + this._session = null; + this._setState('revoked'); + setTimeout(() => this._setState('idle'), 1_000); + } + + dispose(): void { + if (this._pollTimer) { + clearTimeout(this._pollTimer); + } + this._onStateChange.dispose(); + } + + private _setState(state: SessionState): void { + this._state = state; + this._onStateChange.fire(state); + } + + private getAuthUrl(): string { + return vscode.workspace + .getConfiguration('codelink') + .get('authServiceUrl', 'http://localhost:8081'); + } +} diff --git a/packages/vscode-extension/src/diff/DiffGenerator.ts b/packages/vscode-extension/src/diff/DiffGenerator.ts deleted file mode 100644 index a1c1259..0000000 --- a/packages/vscode-extension/src/diff/DiffGenerator.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as vscode from 'vscode'; -import * as fs from 'fs/promises'; -import { FileContextPayload } from '@codelink/protocol'; - -/** - * Diff Generator - * Compares HEAD version vs current file state and prepares payload - */ -export interface DiffGenerator { - /** - * Generate diff data for a file - * @param filePath - Absolute path to the file - * @param headContent - Content from Git HEAD (empty if untracked) - * @returns FileContextPayload or null if error occurs - */ - generateDiff(filePath: string, headContent: string): Promise; -} - -// File size threshold: 1MB -const FILE_SIZE_WARNING_THRESHOLD = 1024 * 1024; // 1MB in bytes -const FILE_SIZE_MAX_THRESHOLD = 10 * 1024 * 1024; // 10MB in bytes - -export class DiffGeneratorImpl implements DiffGenerator { - async generateDiff(filePath: string, headContent: string): Promise { - const startTime = Date.now(); - - try { - // Validate file path - if (!filePath || filePath.trim().length === 0) { - console.warn(`[DiffGenerator] Invalid file path: empty or whitespace-only`); - return null; - } - - // Check file size before reading - const fileStats = await this.getFileStats(filePath); - - if (fileStats.size > FILE_SIZE_MAX_THRESHOLD) { - console.warn( - `[DiffGenerator] File too large (${this.formatBytes(fileStats.size)}), skipping: ${filePath}` - ); - return null; - } - - if (fileStats.size > FILE_SIZE_WARNING_THRESHOLD) { - console.warn( - `[DiffGenerator] Large file detected (${this.formatBytes(fileStats.size)}): ${filePath}` - ); - } - - // Read current file content - prefer editor content over disk - const modifiedFile = await this.getCurrentFileContent(filePath); - - // Get workspace-relative path for fileName - const fileName = vscode.workspace.asRelativePath(filePath, false); - - // Check if file has unsaved changes - const isDirty = this.isFileDirty(filePath); - - // Generate timestamp - const timestamp = Date.now(); - - // Construct FileContextPayload - const payload: FileContextPayload = { - fileName, - originalFile: headContent, - modifiedFile, - isDirty, - timestamp, - }; - - const elapsed = Date.now() - startTime; - console.log( - `[DiffGenerator] Generated diff for ${fileName} (${this.formatBytes(modifiedFile.length)} bytes, isDirty: ${isDirty}, took ${elapsed}ms)` - ); - - // Performance warning if diff generation took too long - if (elapsed > 200) { - console.warn( - `[DiffGenerator] Diff generation exceeded 200ms threshold: ${elapsed}ms for ${fileName}` - ); - } - - return payload; - } catch (error) { - const elapsed = Date.now() - startTime; - console.error( - `[DiffGenerator] Error generating diff for ${filePath} (took ${elapsed}ms):`, - error - ); - return null; - } - } - - /** - * Get file statistics - * @param filePath - Absolute path to the file - * @returns File stats including size - */ - private async getFileStats(filePath: string): Promise<{ size: number }> { - try { - const stats = await fs.stat(filePath); - return { size: stats.size }; - } catch (error) { - console.error(`[DiffGenerator] Error getting file stats ${filePath}:`, error); - throw error; - } - } - - /** - * Format bytes to human-readable string - * @param bytes - Number of bytes - * @returns Formatted string (e.g., "1.5 MB") - */ - private formatBytes(bytes: number): string { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`; - return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; - } - - /** - * Get current file content - prefer editor content over disk - * This ensures we get unsaved changes from the editor - * @param filePath - Absolute path to the file - * @returns File content as string - */ - private async getCurrentFileContent(filePath: string): Promise { - // First, try to get content from open editor - const document = vscode.workspace.textDocuments.find((doc) => doc.uri.fsPath === filePath); - - if (document) { - // Return editor content (includes unsaved changes) - return document.getText(); - } - - // Fall back to reading from disk if not open in editor - return this.readFileContent(filePath); - } - - /** - * Read file content from disk - * @param filePath - Absolute path to the file - * @returns File content as string - */ - private async readFileContent(filePath: string): Promise { - try { - const buffer = await fs.readFile(filePath); - return buffer.toString('utf-8'); - } catch (error) { - console.error(`[DiffGenerator] Error reading file ${filePath}:`, error); - throw error; - } - } - - /** - * Check if file has unsaved changes in the editor - * @param filePath - Absolute path to the file - * @returns true if file has unsaved changes, false otherwise - */ - private isFileDirty(filePath: string): boolean { - // Find the document in VS Code's open documents - const document = vscode.workspace.textDocuments.find((doc) => doc.uri.fsPath === filePath); - - // If document is open, check its dirty state - if (document) { - return document.isDirty; - } - - // If document is not open, it's not dirty - return false; - } -} diff --git a/packages/vscode-extension/src/diff/FileWatcher.ts b/packages/vscode-extension/src/diff/FileWatcher.ts new file mode 100644 index 0000000..d53a9a1 --- /dev/null +++ b/packages/vscode-extension/src/diff/FileWatcher.ts @@ -0,0 +1,158 @@ +import * as vscode from 'vscode'; +import * as path from 'path'; +import { GitIntegrationModule } from '../git/GitIntegrationModule'; +import { SnapshotEngine } from './SnapshotEngine'; +import { PatchEncoder } from './PatchEncoder'; +import { WsClient } from '../websocket/WsClient'; + +const DEBOUNCE_MS = 300; + +export class FileWatcher { + private disposables: vscode.Disposable[] = []; + private debounceTimers = new Map(); + private activeFile: string | null = null; + + constructor( + private readonly git: GitIntegrationModule, + private readonly snapshot: SnapshotEngine, + private readonly patches: PatchEncoder, + private readonly ws: WsClient + ) {} + + start(workspaceRoot: string): void { + this.git.initialize(workspaceRoot); + + this.disposables.push( + vscode.window.onDidChangeActiveTextEditor((editor) => { + if (editor?.document) { + this.onFileSwitch(editor.document); + } + }) + ); + + this.disposables.push( + vscode.workspace.onDidSaveTextDocument((doc) => { + this.scheduleSync(doc, false); + }) + ); + + this.disposables.push( + vscode.workspace.onDidChangeTextDocument((event) => { + this.scheduleSync(event.document, true); + }) + ); + + if (vscode.window.activeTextEditor?.document) { + this.onFileSwitch(vscode.window.activeTextEditor.document); + } + } + + stop(): void { + this.debounceTimers.forEach((t) => clearTimeout(t)); + this.debounceTimers.clear(); + this.disposables.forEach((d) => d.dispose()); + this.disposables = []; + } + + private async onFileSwitch(doc: vscode.TextDocument): Promise { + const fileName = this.getRelativeName(doc); + if (!fileName || this.activeFile === fileName) { + return; + } + + this.activeFile = fileName; + this.patches.reset(fileName); + await this.sendSnapshot(doc); + } + + private scheduleSync(doc: vscode.TextDocument, isDirty: boolean): void { + if (!this.ws.isConnected()) { + return; + } + + const fileName = this.getRelativeName(doc); + if (!fileName) { + return; + } + + if (!this.debounceTimers.has(fileName)) { + this.sendPatchOrSnapshot(doc, isDirty); + } + + const existing = this.debounceTimers.get(fileName); + if (existing) { + clearTimeout(existing); + } + + const timer = setTimeout(() => { + this.debounceTimers.delete(fileName); + }, DEBOUNCE_MS); + + this.debounceTimers.set(fileName, timer); + } + + private async sendPatchOrSnapshot(doc: vscode.TextDocument, isDirty: boolean): Promise { + const fileName = this.getRelativeName(doc); + if (!fileName) { + return; + } + + const content = doc.getText(); + const patch = this.patches.encode(fileName, content, isDirty); + + if (patch) { + this.ws.send('FILE_PATCH', patch); + } else { + await this.sendSnapshot(doc); + } + } + + private async sendSnapshot(doc: vscode.TextDocument): Promise { + const fileName = this.getRelativeName(doc); + if (!fileName) { + return; + } + + const content = doc.getText(); + const isDirty = doc.isDirty; + const gitHead = await this.git.isTracked(doc.fileName); + + const payload = this.snapshot.build(fileName, content, isDirty, gitHead); + + this.patches.recordSnapshot(fileName, content, payload.seq); + + this.ws.send('FILE_SNAPSHOT', payload); + + const editor = vscode.window.activeTextEditor; + if (editor?.document === doc) { + const pos = editor.selection.active; + this.ws.send('EDITOR_FOCUS', { + fileName, + cursorLine: pos.line, + cursorCol: pos.character, + }); + } + } + + async handleSnapshotRequest(fileName: string): Promise { + this.patches.reset(fileName); + this.snapshot.reset(fileName); + + const editor = vscode.window.activeTextEditor; + if (editor && this.getRelativeName(editor.document) === fileName) { + await this.sendSnapshot(editor.document); + } + } + + private getRelativeName(doc: vscode.TextDocument): string | null { + if (doc.uri.scheme != 'file') { + return null; + } + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders?.length) { + return null; + } + const root = workspaceFolders[0].uri.fsPath; + return path.relative(root, doc.fileName).replace(/\\/g, '/'); + } +} diff --git a/packages/vscode-extension/src/diff/PatchEncoder.ts b/packages/vscode-extension/src/diff/PatchEncoder.ts new file mode 100644 index 0000000..0945ea2 --- /dev/null +++ b/packages/vscode-extension/src/diff/PatchEncoder.ts @@ -0,0 +1,54 @@ +import * as dmp from 'diff-match-patch'; + +export interface PatchPayload { + fileName: string; + patches: string; + fromSeq: number; + toSeq: number; + isDirty: boolean; + timestamp: number; +} + +export class PatchEncoder { + private readonly engine = new dmp.diff_match_patch(); + private readonly lastSent = new Map(); + + encode(fileName: string, newContent: string, isDirty: boolean): PatchPayload | null { + const last = this.lastSent.get(fileName); + + if (!last) { + return null; + } + + if (last.content === newContent) { + return null; + } + + const patches = this.engine.patch_make(last.content, newContent); + const patchText = this.engine.patch_toText(patches); + const toSeq = last.seq + 1; + + this.lastSent.set(fileName, { content: newContent, seq: toSeq }); + + return { + fileName, + patches: patchText, + fromSeq: last.seq, + toSeq: toSeq, + isDirty, + timestamp: Date.now(), + }; + } + + recordSnapshot(fileName: string, content: string, seq: number): void { + this.lastSent.set(fileName, { content, seq }); + } + + reset(fileName: string): void { + this.lastSent.delete(fileName); + } + + resetAll(): void { + this.lastSent.clear(); + } +} diff --git a/packages/vscode-extension/src/diff/SnapshotEngine.ts b/packages/vscode-extension/src/diff/SnapshotEngine.ts new file mode 100644 index 0000000..6bd6a2e --- /dev/null +++ b/packages/vscode-extension/src/diff/SnapshotEngine.ts @@ -0,0 +1,47 @@ +import * as zlib from 'zlib'; + +const COMPRESSION_THRESHOLD_BYTES = 64 * 1024; + +export interface SnapshotPayload { + fileName: string; + content: string; + encoding: 'utf8' | 'gzip+base64'; + seq: number; + isDirty: boolean; + gitHead: boolean; + timestamp: number; +} + +export class SnapshotEngine { + private seqCounter = new Map(); + + build(fileName: string, content: string, isDirty: boolean, gitHead: boolean): SnapshotPayload { + const seq = (this.seqCounter.get(fileName) ?? 0) + 1; + this.seqCounter.set(fileName, seq); + + const rawBytes = Buffer.byteLength(content, 'utf8'); + const shouldCompress = rawBytes > COMPRESSION_THRESHOLD_BYTES; + + return { + fileName, + content: shouldCompress ? zlib.gzipSync(content).toString('base64') : content, + encoding: shouldCompress ? 'gzip+base64' : 'utf8', + seq, + isDirty, + gitHead, + timestamp: Date.now(), + }; + } + + currentSeq(fileName: string): number { + return this.seqCounter.get(fileName) ?? 0; + } + + reset(fileName: string): void { + this.seqCounter.delete(fileName); + } + + resetAll(): void { + this.seqCounter.clear(); + } +} diff --git a/packages/vscode-extension/src/editor-adapters/ContinueAdapter.ts b/packages/vscode-extension/src/editor-adapters/ContinueAdapter.ts deleted file mode 100644 index bfbf9f9..0000000 --- a/packages/vscode-extension/src/editor-adapters/ContinueAdapter.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as vscode from 'vscode'; -import { EditorAdapter } from './EditorAdapter'; - -export class ContinueAdapter implements EditorAdapter { - readonly id = 'continue'; - readonly displayName = 'Continue'; - - async isAvailable(): Promise { - const extension = vscode.extensions.getExtension('continue.continue'); - return !!extension; // && extension.isActive; - } - - async injectPrompt(_prompt: string): Promise { - try { - // Continue uses a command to accept input or specific API if available - // For now, we'll try to use the command to focus/open and then insert - // This is a best-effort integration without a public API - - // Attempt to execute the command to open Continue sidebar - await vscode.commands.executeCommand('continue.focusContinueInput'); - - // Wait a brief moment for focus - await new Promise((resolve) => setTimeout(resolve, 500)); - - // We can't easily type into the webview, but we can verify it opened. - // If there's a specific API command to send text, we would use it here. - // Since Continue doesn't expose a simple "send text" command publicly yet, - // we will fallback to a notification instructing the user, or - // look for a more specific command if updated. - - // REVISIT: Check for "continue.sendToChat" or similar in future versions. - - return true; - } catch (error) { - console.error('Failed to inject prompt into Continue:', error); - return false; - } - } -} diff --git a/packages/vscode-extension/src/editor-adapters/EditorAdapter.ts b/packages/vscode-extension/src/editor-adapters/EditorAdapter.ts deleted file mode 100644 index 76c70c6..0000000 --- a/packages/vscode-extension/src/editor-adapters/EditorAdapter.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Interface for AI Editor Adapters - */ -export interface EditorAdapter { - /** - * Unique identifier for the editor (e.g., 'continue', 'kiro', 'cursor') - */ - readonly id: string; - - /** - * Display name of the editor - */ - readonly displayName: string; - - /** - * Check if the editor is available/installed - */ - isAvailable(): Promise; - - /** - * Inject a prompt into the editor - * @param prompt The text prompt to inject - * @returns check if injection was successful - */ - injectPrompt(prompt: string): Promise; -} diff --git a/packages/vscode-extension/src/editor-adapters/EditorRegistry.ts b/packages/vscode-extension/src/editor-adapters/EditorRegistry.ts deleted file mode 100644 index c796f1d..0000000 --- a/packages/vscode-extension/src/editor-adapters/EditorRegistry.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { EditorAdapter } from './EditorAdapter'; -import { ContinueAdapter } from './ContinueAdapter'; -import { KiroAdapter, CursorAdapter, AntigravityAdapter } from './OtherAdapters'; -import { VSCodeAdapter } from './VSCodeAdapter'; - -export class EditorRegistry { - private adapters: EditorAdapter[] = []; - - constructor() { - this.registerAdapters(); - } - - private registerAdapters() { - // Register adapters in priority order - // 1. Full Sync capable editors - this.adapters.push(new ContinueAdapter()); - - // 2. Partial Sync capable editors - this.adapters.push(new KiroAdapter()); - - // 3. Control-Only capable editors - this.adapters.push(new CursorAdapter()); - this.adapters.push(new AntigravityAdapter()); - - // 4. Fallback - this.adapters.push(new VSCodeAdapter()); - } - - /** - * Get the best available editor adapter - */ - async getBestAvailableAdapter(): Promise { - for (const adapter of this.adapters) { - if (await adapter.isAvailable()) { - return adapter; - } - } - return undefined; - } - - /** - * Get all available adapters - */ - async getAvailableAdapters(): Promise { - const available: EditorAdapter[] = []; - for (const adapter of this.adapters) { - if (await adapter.isAvailable()) { - available.push(adapter); - } - } - return available; - } - - /** - * Get adapter by ID - */ - getAdapter(id: string): EditorAdapter | undefined { - return this.adapters.find((a) => a.id === id); - } -} diff --git a/packages/vscode-extension/src/editor-adapters/OtherAdapters.ts b/packages/vscode-extension/src/editor-adapters/OtherAdapters.ts deleted file mode 100644 index cd6ea27..0000000 --- a/packages/vscode-extension/src/editor-adapters/OtherAdapters.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as vscode from 'vscode'; -import { EditorAdapter } from './EditorAdapter'; - -export class KiroAdapter implements EditorAdapter { - readonly id = 'kiro'; - readonly displayName = 'Kiro'; - - async isAvailable(): Promise { - return !!vscode.extensions.getExtension('kiro.kiro'); - } - - async injectPrompt(_prompt: string): Promise { - // Placeholder implementation - return false; - } -} - -export class CursorAdapter implements EditorAdapter { - readonly id = 'cursor'; - readonly displayName = 'Cursor'; - - async isAvailable(): Promise { - // Cursor identifies itself differently, often via env or specific extension - return vscode.env.appName.includes('Cursor'); - } - - async injectPrompt(_prompt: string): Promise { - // Cursor AI command implementation would go here - return false; - } -} - -export class AntigravityAdapter implements EditorAdapter { - readonly id = 'antigravity'; - readonly displayName = 'Antigravity'; - - async isAvailable(): Promise { - return !!vscode.extensions.getExtension('google.antigravity'); - } - - async injectPrompt(_prompt: string): Promise { - // Antigravity implementation - return false; - } -} diff --git a/packages/vscode-extension/src/editor-adapters/VSCodeAdapter.ts b/packages/vscode-extension/src/editor-adapters/VSCodeAdapter.ts deleted file mode 100644 index 650d508..0000000 --- a/packages/vscode-extension/src/editor-adapters/VSCodeAdapter.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as vscode from 'vscode'; -import { EditorAdapter } from './EditorAdapter'; - -export class VSCodeAdapter implements EditorAdapter { - readonly id = 'vscode'; - readonly displayName = 'VS Code'; - - async isAvailable(): Promise { - return true; // Always available - } - - async injectPrompt(prompt: string): Promise { - // Basic fallback: copy to clipboard and show notification - await vscode.env.clipboard.writeText(prompt); - - const selection = await vscode.window.showInformationMessage( - `Received prompt from mobile: "${prompt.substring(0, 50)}..."`, - 'Paste in Editor', - 'Dismiss' - ); - - if (selection === 'Paste in Editor') { - const editor = vscode.window.activeTextEditor; - if (editor) { - editor.edit((editBuilder) => { - editBuilder.insert(editor.selection.active, prompt); - }); - } - } - - return true; - } -} diff --git a/packages/vscode-extension/src/editors/adapters/AntigravityAdapter.test.ts b/packages/vscode-extension/src/editors/adapters/AntigravityAdapter.test.ts new file mode 100644 index 0000000..32a4820 --- /dev/null +++ b/packages/vscode-extension/src/editors/adapters/AntigravityAdapter.test.ts @@ -0,0 +1,112 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { AntigravityAdapter } from './AntigravityAdapter'; + +// Mock VS Code API +const mockGetCommands = vi.fn(); +const mockExecuteCommand = vi.fn(); + +vi.mock('vscode', () => ({ + commands: { + getCommands: () => mockGetCommands(), + executeCommand: (cmd: string, ...args: any[]) => mockExecuteCommand(cmd, ...args), + }, +})); + +describe('AntigravityAdapter', () => { + let adapter: AntigravityAdapter; + + beforeEach(() => { + adapter = new AntigravityAdapter(); + vi.clearAllMocks(); + }); + + describe('detect()', () => { + it('should return isInstalled: true when antigravity commands are available', async () => { + mockGetCommands.mockResolvedValue([ + 'git.clone', + 'antigravity.chat.send', + 'antigravity.chat.open', + ]); + + const result = await adapter.detect(); + + expect(result.isInstalled).toBe(true); + expect(result.availableCommands).toContain('antigravity.chat.send'); + expect(result.availableCommands).toContain('antigravity.chat.open'); + }); + + it('should return isInstalled: false when no antigravity commands are available', async () => { + mockGetCommands.mockResolvedValue([ + 'git.clone', + 'continue.openChat', + ]); + + const result = await adapter.detect(); + + expect(result.isInstalled).toBe(false); + expect(result.availableCommands).toHaveLength(0); + }); + + it('should fail safe and return isInstalled: false if commands query throws an error', async () => { + mockGetCommands.mockRejectedValue(new Error('VS Code internal failure')); + + const result = await adapter.detect(); + + expect(result.isInstalled).toBe(false); + expect(result.availableCommands).toHaveLength(0); + }); + }); + + describe('injectPrompt()', () => { + it('should inject prompt successfully using primary command "antigravity.chat.send"', async () => { + mockExecuteCommand.mockResolvedValue(undefined); // Simulate success + + const result = await adapter.injectPrompt('Hello Antigravity!'); + + expect(result.success).toBe(true); + expect(result.commandUsed).toBe('antigravity.chat.send'); + expect(mockExecuteCommand).toHaveBeenCalledWith('antigravity.chat.send', 'Hello Antigravity!'); + }); + + it('should fallback to "antigravity.chat.open" if primary command fails', async () => { + // Primary fails, Fallback 1 succeeds + mockExecuteCommand + .mockRejectedValueOnce(new Error('Command not found')) + .mockResolvedValueOnce(undefined); + + const result = await adapter.injectPrompt('Fix this file.'); + + expect(result.success).toBe(true); + expect(result.commandUsed).toBe('antigravity.chat.open'); + expect(mockExecuteCommand).toHaveBeenNthCalledWith(1, 'antigravity.chat.send', 'Fix this file.'); + expect(mockExecuteCommand).toHaveBeenNthCalledWith(2, 'antigravity.chat.open', { + message: 'Fix this file.', + }); + }); + + it('should fallback to "antigravity.sendMessage" if both previous commands fail', async () => { + // Primary fails, Fallback 1 fails, Fallback 2 succeeds + mockExecuteCommand + .mockRejectedValueOnce(new Error('Command not found')) + .mockRejectedValueOnce(new Error('Command not found')) + .mockResolvedValueOnce(undefined); + + const result = await adapter.injectPrompt('Add comments.'); + + expect(result.success).toBe(true); + expect(result.commandUsed).toBe('antigravity.sendMessage'); + expect(mockExecuteCommand).toHaveBeenNthCalledWith(3, 'antigravity.sendMessage', 'Add comments.'); + }); + + it('should fail safe and return success: false if all fallbacks fail', async () => { + // All fail + mockExecuteCommand.mockRejectedValue(new Error('Extension offline')); + + const result = await adapter.injectPrompt('Optimize imports.'); + + expect(result.success).toBe(false); + expect(result.commandUsed).toBe('antigravity.chat.send (attempted)'); + expect(result.error).toContain('Failed to inject prompt into Antigravity'); + }); + }); +}); diff --git a/packages/vscode-extension/src/extension.ts b/packages/vscode-extension/src/extension.ts index a06dd3d..6803ede 100644 --- a/packages/vscode-extension/src/extension.ts +++ b/packages/vscode-extension/src/extension.ts @@ -1,457 +1,134 @@ import * as vscode from 'vscode'; -import * as zlib from 'zlib'; -import * as crypto from 'crypto'; -import { promisify } from 'util'; -import { - SyncFullContextMessage, - FileContextPayload, - InjectPromptMessage, - InjectPromptResponse, - ProtocolMessage, -} from '@codelink/protocol'; -import { FileWatcher } from './watcher/FileWatcher'; -import { GitIntegrationModuleImpl } from './git/GitIntegrationModule'; -import { DiffGeneratorImpl } from './diff/DiffGenerator'; -import { WebSocketClient } from './websocket/WebSocketClient'; +import { KeyManager } from './auth/KeyManager'; +import { LaptopIdentity } from './auth/LaptopIdentity'; import { EditorRegistry } from './editors/adapters/EditorRegistry'; import { ContinueAdapter } from './editors/adapters/ContinueAdapter'; import { KiroAdapter } from './editors/adapters/KiroAdapter'; import { CursorAdapter } from './editors/adapters/CursorAdapter'; import { AntigravityAdapter } from './editors/adapters/AntigravityAdapter'; +import { SessionManager } from './auth/SessionManager'; +import { WsClient } from './websocket/WsClient'; +import { FileWatcher } from './diff/FileWatcher'; +import { GitIntegrationModuleImpl } from './git/GitIntegrationModule'; +import { SnapshotEngine } from './diff/SnapshotEngine'; +import { PatchEncoder } from './diff/PatchEncoder'; +import { PairingWebviewPanel } from './pairing/PairingWebviewPanel'; +import { isInjectPromptPayload, isSnapshotRequestPayload } from '@codelink/protocol'; -// Promisify zlib functions -const gzip = promisify(zlib.gzip); - -// Compression threshold: 50KB -const COMPRESSION_THRESHOLD = 50 * 1024; - -// Global instances -let fileWatcher: FileWatcher; -let gitModule: GitIntegrationModuleImpl; -let diffGenerator: DiffGeneratorImpl; -let wsClient: WebSocketClient; -let editorRegistry: EditorRegistry; -let outputChannel: vscode.OutputChannel; - -export async function activate(context: vscode.ExtensionContext) { - // Create output channel for logging - outputChannel = vscode.window.createOutputChannel('CodeLink'); - outputChannel.appendLine('CodeLink extension activating...'); +export async function activate(context: vscode.ExtensionContext): Promise { + // Auth + const keyManager = new KeyManager(context.secrets); + const laptopIdentity = new LaptopIdentity(keyManager, context.globalState); - // Initialize all modules + let laptopId: string; try { - await initializeModules(context); - outputChannel.appendLine('CodeLink extension activated successfully'); - } catch (error) { - outputChannel.appendLine(`Error activating CodeLink: ${error}`); - vscode.window.showErrorMessage('Failed to activate CodeLink extension'); - } - - // Register hello command for testing - const disposable = vscode.commands.registerCommand('codelink.hello', () => { - vscode.window.showInformationMessage('CodeLink Extension Active!'); - }); - - context.subscriptions.push(disposable); - context.subscriptions.push(outputChannel); -} - -async function initializeModules(context: vscode.ExtensionContext): Promise { - // Get workspace root - const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length === 0) { - outputChannel.appendLine('No workspace folder found, skipping initialization'); + laptopId = await laptopIdentity.ensureRegistered(); + } catch (err) { + vscode.window.showErrorMessage( + `CodeLink: Could not register with Auth Service. ` + + `Check codelink.authServiceUrl in settings. Error: ${err}.` + ); return; } - const workspaceRoot = workspaceFolders[0].uri.fsPath; - outputChannel.appendLine(`Workspace root: ${workspaceRoot}`); - - // Initialize Editor Registry - editorRegistry = new EditorRegistry(); - - // Register all editor adapters - editorRegistry.register(new ContinueAdapter()); - editorRegistry.register(new KiroAdapter()); - editorRegistry.register(new CursorAdapter()); - editorRegistry.register(new AntigravityAdapter()); - - outputChannel.appendLine('Editor registry initialized with 4 adapters'); - - // Run initial editor detection - try { - const detectionResults = await editorRegistry.detectAll(); - outputChannel.appendLine('Editor detection completed:'); - - for (const [editorId, result] of detectionResults) { - if (result.isInstalled) { - outputChannel.appendLine( - ` - ${editorId}: installed (${result.availableCommands?.length || 0} commands)` - ); - } else { - outputChannel.appendLine(` - ${editorId}: not installed`); + // Editor Adapters + const registry = new EditorRegistry(); + registry.register(new ContinueAdapter()); + registry.register(new KiroAdapter()); + registry.register(new CursorAdapter()); + registry.register(new AntigravityAdapter()); + + // Session + const sessionManager = new SessionManager(keyManager, laptopId); + context.subscriptions.push({ dispose: () => sessionManager.dispose() }); + + // WebSocket Client + const wsClient = new WsClient({ + onConnected: () => { + vscode.window.setStatusBarMessage('$(plug) CodeLink: Mobile connected', 3_000); + }, + onDisconnected: () => { + vscode.window.setStatusBarMessage('$(debug-disconnected) CodeLink: Disconnected', 3_000); + }, + onMessage: async (type, payload, id) => { + switch (type) { + case 'SNAPSHOT_REQUEST': { + if (!isSnapshotRequestPayload(payload)) { + return; + } + const { fileName } = payload as { fileName: string }; + await fileWatcher.handleSnapshotRequest(fileName); + break; + } + case 'PATCH_ACK': + break; + case 'INJECT_PROMPT': { + if (!isInjectPromptPayload(payload)) { + return; + } + const { prompt } = payload as { prompt: string }; + const adapter = await registry.getBestAdapter(); + if (!adapter) { + wsClient.send('PROMPT_RESPONSE', { + originalId: id, + success: false, + error: 'No AI editor detected', + }); + return; + } + const result = await adapter.injectPrompt(prompt); + wsClient.send('PROMPT_RESPONSE', { + originalId: id, + success: result.success, + editorUsed: adapter.editorName, + error: result.error, + }); + break; + } + case 'SESSION_REVOKED': { + wsClient.disconnect(); + vscode.window.showInformationMessage('CodeLink: Session ended.'); + break; + } } - } - - // Log the best available adapter - const bestAdapter = await editorRegistry.getBestAdapter(); - if (bestAdapter) { - outputChannel.appendLine( - `Best available editor: ${bestAdapter.editorName} (${bestAdapter.capabilities.syncLevel} sync)` - ); - } else { - outputChannel.appendLine('No AI editor detected'); - } - } catch (error) { - outputChannel.appendLine(`Error during editor detection: ${error}`); - } - - // Store registry in extension context for access by other modules - context.globalState.update('editorRegistry', editorRegistry); - - // Initialize Git Integration Module - gitModule = new GitIntegrationModuleImpl(); - const gitInitialized = await gitModule.initialize(workspaceRoot); - - if (gitInitialized) { - outputChannel.appendLine('Git integration initialized successfully'); - } else { - outputChannel.appendLine('Git repository not found, diffs will show full file as additions'); - } - - // Initialize Diff Generator - diffGenerator = new DiffGeneratorImpl(); - outputChannel.appendLine('Diff generator initialized'); - - // Initialize WebSocket Client - // TODO: Make relay server URL configurable via settings - // Get relay server URL from configuration - const config = vscode.workspace.getConfiguration('codelink'); - const relayServerUrl = config.get('relayServerUrl') || 'http://localhost:8080'; - wsClient = new WebSocketClient(); - wsClient.connect(relayServerUrl); - outputChannel.appendLine(`WebSocket client connecting to ${relayServerUrl}`); - - // Register handler for incoming messages (e.g., INJECT_PROMPT from mobile) - wsClient.onMessage(handleIncomingMessage); - - // Initialize File Watcher - fileWatcher = new FileWatcher(); - fileWatcher.onFileChanged = handleFileChanged; - fileWatcher.initialize(); - outputChannel.appendLine('File watcher initialized'); - - // Register for cleanup - context.subscriptions.push({ - dispose: () => { - fileWatcher.dispose(); - wsClient.disconnect(); - editorRegistry.clearCache(); }, }); -} - -/** - * Handle incoming messages from the WebSocket (e.g., from mobile client) - */ -async function handleIncomingMessage(message: ProtocolMessage): Promise { - try { - outputChannel.appendLine(`[INFO] Received message type: ${message.type}`); - if (message.type === 'INJECT_PROMPT') { - await handlePromptInjection(message as InjectPromptMessage); - } - // Add handlers for other message types as needed - } catch (error) { - outputChannel.appendLine(`[ERROR] Error handling incoming message: ${error}`); - console.error('Error handling incoming message:', error); - } -} + context.subscriptions.push({ dispose: () => wsClient.disconnect() }); -/** - * Handle prompt injection request from mobile client - */ -async function handlePromptInjection(message: InjectPromptMessage): Promise { - const startTime = Date.now(); - outputChannel.appendLine( - `[INFO] Handling prompt injection: "${message.payload.prompt.substring(0, 50)}..."` - ); - - try { - // Get the best available editor adapter - const adapter = await editorRegistry.getBestAdapter(); - - if (!adapter) { - const errorResponse: InjectPromptResponse = { - id: crypto.randomUUID(), - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: message.id, - payload: { - success: false, - error: - 'No AI editor is installed. Please install Continue, Kiro, Cursor, or Antigravity.', - }, - }; - wsClient.send(errorResponse); - outputChannel.appendLine(`[ERROR] No AI editor available for prompt injection`); - return; - } - - outputChannel.appendLine( - `[INFO] Using editor: ${adapter.editorName} (${adapter.capabilities.syncLevel} sync)` - ); + // Diff Engine + const git = new GitIntegrationModuleImpl(); + const snapshot = new SnapshotEngine(); + const patches = new PatchEncoder(); + const fileWatcher = new FileWatcher(git, snapshot, patches, wsClient); - if (!adapter.capabilities.canInjectPrompt) { - const errorResponse: InjectPromptResponse = { - id: crypto.randomUUID(), - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: message.id, - payload: { - success: false, - error: `Editor ${adapter.editorName} does not support prompt injection`, - editorUsed: adapter.editorName, - }, - }; - wsClient.send(errorResponse); - outputChannel.appendLine( - `[ERROR] Editor ${adapter.editorName} does not support prompt injection` - ); - return; - } - - const result = await adapter.injectPrompt(message.payload.prompt); - const elapsed = Date.now() - startTime; - - const response: InjectPromptResponse = { - id: crypto.randomUUID(), - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: message.id, - payload: { - success: result.success, - error: result.error, - editorUsed: adapter.editorName, - }, - }; - wsClient.send(response); - - if (result.success) { - outputChannel.appendLine( - `[INFO] Prompt injection successful (${elapsed}ms) using ${result.commandUsed}` - ); - } else { - outputChannel.appendLine(`[ERROR] Prompt injection failed (${elapsed}ms): ${result.error}`); - } - } catch (error) { - const elapsed = Date.now() - startTime; - const errorResponse: InjectPromptResponse = { - id: crypto.randomUUID(), - timestamp: Date.now(), - type: 'INJECT_PROMPT_RESPONSE', - originalId: message.id, - payload: { - success: false, - error: `Unexpected error during prompt injection: ${error}`, - }, - }; - wsClient.send(errorResponse); - outputChannel.appendLine( - `[ERROR] Unexpected error during prompt injection (${elapsed}ms): ${error}` - ); + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + if (workspaceRoot) { + fileWatcher.start(workspaceRoot); } -} - -/** - * Handle file change events from the File Watcher - * This is the main pipeline: File Watcher β†’ Git β†’ Diff Generator β†’ WebSocket - */ -async function handleFileChanged(filePath: string): Promise { - const pipelineStartTime = Date.now(); - - try { - outputChannel.appendLine(`[INFO] File changed: ${filePath}`); - - // Step 1: Fetch HEAD version from Git - const gitStartTime = Date.now(); - let headContent = ''; - try { - headContent = await gitModule.getHeadVersion(filePath); - const gitElapsed = Date.now() - gitStartTime; - outputChannel.appendLine( - `[PERF] Git operation: ${gitElapsed}ms (HEAD content: ${headContent.length} bytes)` - ); - } catch (error) { - const gitElapsed = Date.now() - gitStartTime; - outputChannel.appendLine(`[ERROR] Failed to fetch HEAD version (${gitElapsed}ms): ${error}`); - // Continue with empty HEAD content (treat as untracked file) - } - - // Step 2: Generate diff - const diffStartTime = Date.now(); - const payload = await diffGenerator.generateDiff(filePath, headContent); - const diffElapsed = Date.now() - diffStartTime; - - if (!payload) { - outputChannel.appendLine(`[WARN] Failed to generate diff (${diffElapsed}ms), skipping`); - return; - } - outputChannel.appendLine( - `[PERF] Diff generation: ${diffElapsed}ms (${payload.fileName}, isDirty: ${payload.isDirty})` - ); - - // Step 3: Apply compression if payload is large - const compressionStartTime = Date.now(); - const compressedPayload = await compressPayloadIfNeeded(payload); - const compressionElapsed = Date.now() - compressionStartTime; + context.subscriptions.push({ dispose: () => fileWatcher.stop() }); - if (compressedPayload.compressed) { - outputChannel.appendLine( - `[PERF] Compression: ${compressionElapsed}ms (${compressedPayload.originalSize} β†’ ${compressedPayload.compressedSize} bytes, ${compressedPayload.compressionRatio}% reduction)` - ); - } + // Commands + context.subscriptions.push( + vscode.commands.registerCommand('codelink.startPairing', () => { + PairingWebviewPanel.createOrShow(context, sessionManager, wsClient); + }), - // Step 4: Create SYNC_FULL_CONTEXT message - const message: SyncFullContextMessage = { - id: `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`, - timestamp: Date.now(), - type: 'SYNC_FULL_CONTEXT', - payload: compressedPayload.payload, - }; - - // Step 5: Send message via WebSocket - const wsStartTime = Date.now(); - try { - wsClient.send(message); - const wsElapsed = Date.now() - wsStartTime; - outputChannel.appendLine(`[PERF] WebSocket send: ${wsElapsed}ms (message id: ${message.id})`); - } catch (error) { - const wsElapsed = Date.now() - wsStartTime; - outputChannel.appendLine(`[ERROR] Failed to send message (${wsElapsed}ms): ${error}`); - throw error; - } - - // Log total pipeline time - const totalElapsed = Date.now() - pipelineStartTime; - outputChannel.appendLine(`[PERF] Total pipeline: ${totalElapsed}ms`); - - // Performance warning if total time exceeds 2000ms - if (totalElapsed > 2000) { - outputChannel.appendLine( - `[WARN] Pipeline exceeded 2000ms threshold: ${totalElapsed}ms for ${payload.fileName}` + vscode.commands.registerCommand('codelink.revokeSession', async () => { + await sessionManager.revokeSession(); + wsClient.disconnect(); + vscode.window.showInformationMessage('CodeLink: Session revoked.'); + }), + + vscode.commands.registerCommand('codelink.showStatus', () => { + const state = sessionManager.state; + const sess = sessionManager.session; + vscode.window.showInformationMessage( + `CodeLink status ${state}` + (sess ? ` | session: ${sess.sessionId.slice(0, 12)}...` : '') ); - } - } catch (error) { - const totalElapsed = Date.now() - pipelineStartTime; - outputChannel.appendLine(`[ERROR] Pipeline error (${totalElapsed}ms): ${error}`); - console.error('Error handling file change:', error); - // Don't throw - continue monitoring other files - } -} - -export function deactivate() { - if (fileWatcher) { - fileWatcher.dispose(); - } - if (wsClient) { - wsClient.disconnect(); - } - if (editorRegistry) { - editorRegistry.clearCache(); - } - if (outputChannel) { - outputChannel.appendLine('CodeLink extension deactivated'); - } -} - -/** - * Get the editor registry instance. - * - * This function provides access to the editor registry for other modules - * that need to interact with AI editors (e.g., WebSocket handlers for - * mobile prompt injection). - * - * @returns The editor registry instance, or undefined if not initialized - */ -export function getEditorRegistry(): EditorRegistry | undefined { - return editorRegistry; -} - -/** - * Reset the editor registry (for testing purposes only). - * - * @internal - */ -export function resetEditorRegistry(): void { - editorRegistry = undefined as unknown as EditorRegistry; + }) + ); } -/** - * Compress payload if it exceeds the threshold - * @param payload - The FileContextPayload to potentially compress - * @returns Compressed payload info with metadata - */ -async function compressPayloadIfNeeded(payload: FileContextPayload): Promise<{ - payload: FileContextPayload; - compressed: boolean; - originalSize: number; - compressedSize: number; - compressionRatio: number; -}> { - // Calculate payload size - const payloadString = JSON.stringify(payload); - const originalSize = Buffer.byteLength(payloadString, 'utf-8'); - - // Only compress if payload exceeds threshold - if (originalSize < COMPRESSION_THRESHOLD) { - return { - payload, - compressed: false, - originalSize, - compressedSize: originalSize, - compressionRatio: 0, - }; - } - - try { - // Compress originalFile and modifiedFile separately - const originalFileBuffer = Buffer.from(payload.originalFile, 'utf-8'); - const modifiedFileBuffer = Buffer.from(payload.modifiedFile, 'utf-8'); - - const [compressedOriginal, compressedModified] = await Promise.all([ - gzip(originalFileBuffer), - gzip(modifiedFileBuffer), - ]); - - // Create compressed payload with base64-encoded compressed data - const compressedPayload: FileContextPayload = { - ...payload, - originalFile: compressedOriginal.toString('base64'), - modifiedFile: compressedModified.toString('base64'), - // Add metadata to indicate compression (extend interface if needed) - }; - - const compressedString = JSON.stringify(compressedPayload); - const compressedSize = Buffer.byteLength(compressedString, 'utf-8'); - const compressionRatio = Math.round(((originalSize - compressedSize) / originalSize) * 100); - - return { - payload: compressedPayload, - compressed: true, - originalSize, - compressedSize, - compressionRatio, - }; - } catch (error) { - outputChannel.appendLine(`[WARN] Compression failed, sending uncompressed: ${error}`); - return { - payload, - compressed: false, - originalSize, - compressedSize: originalSize, - compressionRatio: 0, - }; - } -} +export function deactivate(): void {} diff --git a/packages/vscode-extension/src/pairing/PairingWebviewPanel.ts b/packages/vscode-extension/src/pairing/PairingWebviewPanel.ts new file mode 100644 index 0000000..1ac9d65 --- /dev/null +++ b/packages/vscode-extension/src/pairing/PairingWebviewPanel.ts @@ -0,0 +1,189 @@ +import * as vscode from 'vscode'; +import * as qrcode from 'qrcode'; +import { SessionManager } from '../auth/SessionManager'; +import { WsClient } from '../websocket/WsClient'; + +/** + * VS Code Webview panel that displays the QR code for mobile pairing. + * + * Lifecycle: + * 1. Extension calls createOrShow() + * 2. Panel creates a session on the Auth Service + * 3. Renders QR code with 60-second countdown + * 4. Polls Auth Service every 2s via SessionManager.waitForMobile() + * 5. On success: connects WsClient, shows "Paired βœ“", offers Revoke button + * 6. On timeout: auto-refreshes (new QR) + */ + +export class PairingWebviewPanel { + private static instance: PairingWebviewPanel | undefined; + + private readonly panel: vscode.WebviewPanel; + private disposed = false; + + static createOrShow( + context: vscode.ExtensionContext, + sessionManager: SessionManager, + wsClient: WsClient + ): void { + if (PairingWebviewPanel.instance) { + PairingWebviewPanel.instance.panel.reveal(); + return; + } + + const panel = vscode.window.createWebviewPanel( + 'codelinkPairing', + 'CodeLink - Pair Mobile', + vscode.ViewColumn.Beside, + { enableScripts: true, retainContextWhenHidden: true } + ); + + PairingWebviewPanel.instance = new PairingWebviewPanel( + panel, + context, + sessionManager, + wsClient + ); + } + + private constructor( + panel: vscode.WebviewPanel, + private readonly context: vscode.ExtensionContext, + private readonly sessionManager: SessionManager, + private readonly wsClient: WsClient + ) { + this.panel = panel; + + panel.onDidDispose( + () => { + this.disposed = true; + PairingWebviewPanel.instance = undefined; + }, + null, + context.subscriptions + ); + + panel.webview.onDidReceiveMessage((msg) => { + if (msg.command === 'revoke') { + this.sessionManager.revokeSession(); + } else if (msg.command === 'refresh') { + this.startPairingFlow(); + } + }); + + this.startPairingFlow(); + } + + private async startPairingFlow(): Promise { + if (this.disposed) { + return; + } + + try { + this.panel.webview.html = this.loadingHtml('Creating Session...'); + + const { sessionId, qrPayload, expiresAt } = await this.sessionManager.createSession(); + + const qrDataUrl = await qrcode.toDataURL(qrPayload, { width: 280, margin: 2 }); + + if (this.disposed) { + return; + } + this.panel.webview.html = this.pairingHtml(qrDataUrl, expiresAt, qrPayload); + + const session = await this.sessionManager.waitForMobile(sessionId, 90_000); + + if (this.disposed) { + return; + } + + this.wsClient.connect(session.relayWssUrl, session.laptopToken); + this.panel.webview.html = this.pairedHtml(session.sessionId); + } catch (err: unknown) { + if (this.disposed) { + return; + } + const msg = err instanceof Error ? err.message : String(err); + + if (msg.includes('timed out')) { + this.startPairingFlow(); + } else { + this.panel.webview.html = this.errorHtml(msg); + } + } + } + + // HTML Templates + private loadingHtml(message: string): string { + return this.wrap(`

${message}

`); + } + + private pairingHtml(qrDataUrl: string, expiresAt: number, qrPayload: string): string { + const secondsLeft = Math.max(0, Math.round((expiresAt - Date.now()) / 1000)); + return this.wrap(` +

Scan with your phone

+ QR code +

Code expires in ${secondsLeft}s

+ + + + `); + } + + private pairedHtml(sessionId: string): string { + return this.wrap(` +

βœ“ Mobile paired

+

Session: ${sessionId.slice(0, 16)}…

+ + + `); + } + + private errorHtml(message: string): string { + return this.wrap(` +

⚠ Error

+

${message}

+ + + `); + } + + private wrap(body: string): string { + return ` + + + + + +${body} +`; + } +} diff --git a/packages/vscode-extension/src/watcher/FileWatcher.ts b/packages/vscode-extension/src/watcher/FileWatcher.ts deleted file mode 100644 index ad2f1fa..0000000 --- a/packages/vscode-extension/src/watcher/FileWatcher.ts +++ /dev/null @@ -1,155 +0,0 @@ -import * as vscode from 'vscode'; - -/** - * FileWatcher monitors active editor changes and triggers diff generation - * after a debounce period. It tracks the currently active file and only - * triggers events for files within the workspace. - */ -export class FileWatcher { - private debounceTimer: NodeJS.Timeout | null = null; - private activeFilePath: string | null = null; - private disposables: vscode.Disposable[] = []; - private readonly debounceDelay: number = 1000; // 1000ms as per requirements - private lastChangeTime: number = 0; // Track last change for timing verification - - /** - * Callback invoked after debounce period expires - */ - public onFileChanged: (filePath: string) => void = () => {}; - - /** - * Initialize the watcher and register VS Code event listeners - */ - public initialize(): void { - // Subscribe to active editor changes - const editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor( - this.handleActiveEditorChange.bind(this) - ); - this.disposables.push(editorChangeDisposable); - - // Subscribe to text document changes - const documentChangeDisposable = vscode.workspace.onDidChangeTextDocument( - this.handleTextDocumentChange.bind(this) - ); - this.disposables.push(documentChangeDisposable); - - // Set initial active file if one exists - if (vscode.window.activeTextEditor) { - this.activeFilePath = vscode.window.activeTextEditor.document.uri.fsPath; - } - } - - /** - * Handle active text editor changes - */ - private handleActiveEditorChange(editor: vscode.TextEditor | undefined): void { - if (!editor) { - // No active editor - unregister current file - this.activeFilePath = null; - this.clearDebounceTimer(); - return; - } - - const filePath = editor.document.uri.fsPath; - - // Only track files within workspace - if (!this.isWithinWorkspace(filePath)) { - return; - } - - this.activeFilePath = filePath; - - // Trigger diff generation immediately when switching files - this.triggerFileChanged(filePath); - } - - /** - * Handle text document changes - */ - private handleTextDocumentChange(event: vscode.TextDocumentChangeEvent): void { - const filePath = event.document.uri.fsPath; - - // Only process changes for the active file - if (filePath !== this.activeFilePath) { - return; - } - - // Only track files within workspace - if (!this.isWithinWorkspace(filePath)) { - return; - } - - // Debounce the change event - this.debounceFileChange(filePath); - } - - /** - * Debounce file change events - */ - private debounceFileChange(filePath: string): void { - // Track when the change occurred - this.lastChangeTime = Date.now(); - - // Clear existing timer - this.clearDebounceTimer(); - - // Set new timer - this.debounceTimer = setTimeout(() => { - // Verify debounce timing (1000ms Β± 50ms tolerance) - const actualDelay = Date.now() - this.lastChangeTime; - const tolerance = 50; - - if (Math.abs(actualDelay - this.debounceDelay) > tolerance) { - console.warn( - `[FileWatcher] Debounce timing outside tolerance: ${actualDelay}ms (expected ${this.debounceDelay}ms Β± ${tolerance}ms)` - ); - } - - this.triggerFileChanged(filePath); - }, this.debounceDelay); - } - - /** - * Trigger the file changed callback - */ - private triggerFileChanged(filePath: string): void { - this.clearDebounceTimer(); - this.onFileChanged(filePath); - } - - /** - * Clear the debounce timer - */ - private clearDebounceTimer(): void { - if (this.debounceTimer) { - clearTimeout(this.debounceTimer); - this.debounceTimer = null; - } - } - - /** - * Check if a file is within the workspace - */ - private isWithinWorkspace(filePath: string): boolean { - const workspaceFolders = vscode.workspace.workspaceFolders; - - if (!workspaceFolders || workspaceFolders.length === 0) { - return false; - } - - return workspaceFolders.some((folder) => { - const workspacePath = folder.uri.fsPath; - return filePath.startsWith(workspacePath); - }); - } - - /** - * Clean up resources and unregister listeners - */ - public dispose(): void { - this.clearDebounceTimer(); - this.disposables.forEach((disposable) => disposable.dispose()); - this.disposables = []; - this.activeFilePath = null; - } -} diff --git a/packages/vscode-extension/src/websocket/WebSocketClient.ts b/packages/vscode-extension/src/websocket/WebSocketClient.ts deleted file mode 100644 index 9e7122f..0000000 --- a/packages/vscode-extension/src/websocket/WebSocketClient.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { io, Socket } from 'socket.io-client'; -import { ProtocolMessage } from '@codelink/protocol'; - -/** - * Message handler callback type for incoming messages - */ -export type MessageHandler = (message: ProtocolMessage) => void | Promise; - -/** - * WebSocketClient manages the connection to the relay server and handles - * message transmission with queueing and retry logic. - */ -export class WebSocketClient { - private socket: Socket | null = null; - private messageQueue: ProtocolMessage[] = []; - private readonly maxQueueSize = 100; - private reconnectAttempts = 0; - private readonly maxReconnectAttempts = 10; - private readonly baseRetryDelay = 1000; // 1 second - private isConnecting = false; - private messageHandlers: MessageHandler[] = []; - - /** - * Initialize and connect to the relay server - */ - public connect(url: string): void { - if (this.socket || this.isConnecting) { - return; - } - - this.isConnecting = true; - - this.socket = io(url, { - reconnection: true, - reconnectionDelay: this.baseRetryDelay, - reconnectionDelayMax: 5000, - reconnectionAttempts: this.maxReconnectAttempts, - }); - - this.setupEventHandlers(); - } - - /** - * Set up socket event handlers - */ - private setupEventHandlers(): void { - if (!this.socket) { - return; - } - - this.socket.on('connect', () => { - this.isConnecting = false; - this.reconnectAttempts = 0; - this.flushMessageQueue(); - - // Re-bind message handlers - this.messageHandlers.forEach((handler) => { - this.socket!.on('message', (data: string) => { - try { - const parsed = JSON.parse(data); - handler(parsed); - } catch (error) { - console.error('Error parsing message:', error); - } - }); - }); - }); - - this.socket.on('disconnect', (reason) => { - console.log(`WebSocket disconnected: ${reason}`); - }); - - this.socket.on('connect_error', (error) => { - this.isConnecting = false; - this.reconnectAttempts++; - - if (this.reconnectAttempts >= this.maxReconnectAttempts) { - console.error('Max reconnection attempts reached'); - return; - } - - const delay = this.calculateBackoffDelay(); - console.log(`Connection error, retrying in ${delay}ms...`, error.message); - }); - - this.socket.on('error', (error) => { - console.error('WebSocket error:', error); - }); - - // Handle incoming messages - this.socket.on('message', (data: string) => { - try { - const message = JSON.parse(data) as ProtocolMessage; - this.handleIncomingMessage(message); - } catch (error) { - console.error('Error parsing incoming message:', error); - } - }); - } - - /** - * Handle incoming messages by notifying all registered handlers - */ - private handleIncomingMessage(message: ProtocolMessage): void { - this.messageHandlers.forEach((handler) => { - try { - handler(message); - } catch (error) { - console.error('Error in message handler:', error); - } - }); - } - - /** - * Register a message handler for incoming messages - */ - public onMessage(handler: MessageHandler): void { - this.messageHandlers.push(handler); - } - - /** - * Remove a message handler - */ - public offMessage(handler: MessageHandler): void { - const index = this.messageHandlers.indexOf(handler); - if (index !== -1) { - this.messageHandlers.splice(index, 1); - } - } - - /** - * Calculate exponential backoff delay - */ - private calculateBackoffDelay(): number { - return Math.min(this.baseRetryDelay * Math.pow(2, this.reconnectAttempts), 5000); - } - - /** - * Send a message to the relay server - */ - public send(message: ProtocolMessage): void { - if (this.isConnected()) { - this.socket!.emit('message', JSON.stringify(message)); - } else { - this.queueMessage(message); - } - } - - /** - * Queue a message for later transmission - */ - private queueMessage(message: ProtocolMessage): void { - if (this.messageQueue.length >= this.maxQueueSize) { - console.warn('Message queue full, dropping oldest message'); - this.messageQueue.shift(); - } - this.messageQueue.push(message); - } - - /** - * Flush queued messages when connection is restored - */ - private flushMessageQueue(): void { - if (!this.isConnected()) { - return; - } - - const queueLength = this.messageQueue.length; - if (queueLength === 0) { - return; - } - - console.log(`Flushing ${queueLength} queued messages`); - - // Rate limit: send 10 messages per second - const messagesPerBatch = 10; - const batchDelay = 1000; - - const sendBatch = () => { - const batch = this.messageQueue.splice(0, messagesPerBatch); - batch.forEach((msg) => { - if (this.isConnected()) { - this.socket!.emit('message', JSON.stringify(msg)); - } - }); - - if (this.messageQueue.length > 0 && this.isConnected()) { - setTimeout(sendBatch, batchDelay); - } - }; - - sendBatch(); - } - - /** - * Check if connection is active - */ - public isConnected(): boolean { - return this.socket !== null && this.socket.connected; - } - - /** - * Get the number of queued messages - */ - public getQueueSize(): number { - return this.messageQueue.length; - } - - /** - * Disconnect from the relay server - */ - public disconnect(): void { - if (this.socket) { - this.socket.disconnect(); - this.socket = null; - } - this.messageQueue = []; - this.reconnectAttempts = 0; - this.isConnecting = false; - } -} diff --git a/packages/vscode-extension/src/websocket/WsClient.ts b/packages/vscode-extension/src/websocket/WsClient.ts new file mode 100644 index 0000000..632af91 --- /dev/null +++ b/packages/vscode-extension/src/websocket/WsClient.ts @@ -0,0 +1,112 @@ +import WebSocket from 'ws'; +import { buildEnvelope, MessageType, PayloadFor } from '@codelink/protocol'; + +interface WsClientOptions { + onMessage: (type: string, payload: unknown, id: string) => void; + onConnected: () => void; + onDisconnected: () => void; +} + +export class WsClient { + private ws: WebSocket | null = null; + private token: string | null = null; + private url: string | null = null; + private reconnectDelay = 1_000; + private readonly MAX_DELAY = 30_000; + private reconnectTimer: NodeJS.Timeout | null = null; + private sendQueue: string[] = []; + private outSeq = 0; + private connected = false; + private intentionalClose = false; + + constructor(private readonly opts: WsClientOptions) {} + + connect(url: string, token: string): void { + this.url = url; + this.token = token; + this.intentionalClose = false; + this._connect(); + } + + private _connect(): void { + if (!this.url || !this.token) { + return; + } + + const wsUrl = `${this.url}?token=${encodeURIComponent(this.token)}`; + this.ws = new WebSocket(wsUrl, { + perMessageDeflate: true, + }); + + this.ws.on('open', () => { + this.connected = true; + this.reconnectDelay = 1_000; + this.opts.onConnected(); + this.flushQueue(); + }); + + this.ws.on('message', (data: Buffer) => { + try { + const envelope = JSON.parse(data.toString('utf8')) as { + type: string; + payload: unknown; + id: string; + }; + this.opts.onMessage(envelope.type, envelope.payload, envelope.id); + } catch { + // ignore parsing failures + } + }); + + this.ws.on('close', () => { + this.connected = false; + this.opts.onDisconnected(); + if (!this.intentionalClose) { + this.scheduleReconnect(); + } + }); + + this.ws.on('error', () => { + this.ws?.terminate(); + }); + } + + send(type: T, payload: PayloadFor): void { + const envelope = JSON.stringify(buildEnvelope(type, payload)); + + if (this.ws?.readyState === WebSocket.OPEN) { + this.ws.send(envelope); + } else { + this.sendQueue.push(envelope); + } + } + + isConnected(): boolean { + return this.connected; + } + + disconnect(): void { + this.intentionalClose = true; + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + this.ws?.close(1000, 'extension deactivated'); + this.ws = null; + this.connected = false; + } + + private flushQueue(): void { + while (this.sendQueue.length > 0 && this.ws?.readyState === WebSocket.OPEN) { + this.ws.send(this.sendQueue.shift()!); + } + } + + private scheduleReconnect(): void { + const jitter = Math.random() * 500; + this.reconnectTimer = setTimeout(() => { + this._connect(); + }, this.reconnectDelay + jitter); + this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.MAX_DELAY); + } +} diff --git a/packages/vscode-extension/tsconfig.json b/packages/vscode-extension/tsconfig.json index b6e4437..05ed542 100644 --- a/packages/vscode-extension/tsconfig.json +++ b/packages/vscode-extension/tsconfig.json @@ -5,7 +5,8 @@ "rootDir": "./src", "module": "commonjs", "lib": ["ES2020"], - "types": ["vitest/globals", "node"] + "types": ["vitest/globals", "node"], + "composite": true }, "include": ["src/**/*"], "references": [{ "path": "../protocol" }] diff --git a/packages/vscode-extension/tsconfig.tsbuildinfo b/packages/vscode-extension/tsconfig.tsbuildinfo deleted file mode 100644 index 847d88f..0000000 --- a/packages/vscode-extension/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/@types/vscode/index.d.ts","../protocol/dist/index.d.ts","./src/watcher/FileWatcher.ts","../../node_modules/simple-git/dist/src/lib/tasks/diff-name-status.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/task.d.ts","../../node_modules/simple-git/dist/src/lib/types/tasks.d.ts","../../node_modules/simple-git/dist/src/lib/errors/git-error.d.ts","../../node_modules/simple-git/dist/src/lib/types/handlers.d.ts","../../node_modules/simple-git/dist/src/lib/types/index.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/log.d.ts","../../node_modules/simple-git/dist/typings/response.d.ts","../../node_modules/simple-git/dist/src/lib/responses/GetRemoteSummary.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/apply-patch.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/check-is-repo.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/clean.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/clone.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/config.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/count-objects.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/grep.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/reset.d.ts","../../node_modules/simple-git/dist/src/lib/tasks/version.d.ts","../../node_modules/simple-git/dist/typings/types.d.ts","../../node_modules/simple-git/dist/src/lib/errors/git-construct-error.d.ts","../../node_modules/simple-git/dist/src/lib/errors/git-plugin-error.d.ts","../../node_modules/simple-git/dist/src/lib/errors/git-response-error.d.ts","../../node_modules/simple-git/dist/src/lib/errors/task-configuration-error.d.ts","../../node_modules/simple-git/dist/typings/errors.d.ts","../../node_modules/simple-git/dist/typings/simple-git.d.ts","../../node_modules/@simple-git/args-pathspec/dist/src/pathspec.d.ts","../../node_modules/@simple-git/args-pathspec/dist/index.d.ts","../../node_modules/simple-git/dist/typings/index.d.ts","./src/git/GitIntegrationModule.ts","./src/diff/DiffGenerator.ts","../../node_modules/@socket.io/component-emitter/lib/cjs/index.d.ts","../../node_modules/engine.io-parser/build/esm/commons.d.ts","../../node_modules/engine.io-parser/build/esm/encodePacket.d.ts","../../node_modules/engine.io-parser/build/esm/decodePacket.d.ts","../../node_modules/engine.io-parser/build/esm/index.d.ts","../../node_modules/engine.io-client/build/esm/transport.d.ts","../../node_modules/engine.io-client/build/esm/globals.node.d.ts","../../node_modules/engine.io-client/build/esm/socket.d.ts","../../node_modules/engine.io-client/build/esm/transports/polling.d.ts","../../node_modules/engine.io-client/build/esm/transports/polling-xhr.d.ts","../../node_modules/engine.io-client/build/esm/transports/polling-xhr.node.d.ts","../../node_modules/engine.io-client/build/esm/transports/websocket.d.ts","../../node_modules/engine.io-client/build/esm/transports/websocket.node.d.ts","../../node_modules/engine.io-client/build/esm/transports/webtransport.d.ts","../../node_modules/engine.io-client/build/esm/transports/index.d.ts","../../node_modules/engine.io-client/build/esm/util.d.ts","../../node_modules/engine.io-client/build/esm/contrib/parseuri.d.ts","../../node_modules/engine.io-client/build/esm/transports/polling-fetch.d.ts","../../node_modules/engine.io-client/build/esm/index.d.ts","../../node_modules/socket.io-parser/build/esm/index.d.ts","../../node_modules/socket.io-client/build/esm/socket.d.ts","../../node_modules/socket.io-client/build/esm/manager.d.ts","../../node_modules/socket.io-client/build/esm/index.d.ts","./src/websocket/WebSocketClient.ts","./src/editors/adapters/types.ts","./src/editors/adapters/EditorRegistry.ts","./src/editors/adapters/ContinueAdapter.ts","./src/editors/adapters/KiroAdapter.ts","./src/editors/adapters/CursorAdapter.ts","./src/editors/adapters/AntigravityAdapter.ts","./src/extension.ts","./src/editor-adapters/EditorAdapter.ts","./src/editor-adapters/ContinueAdapter.ts","./src/editor-adapters/OtherAdapters.ts","./src/editor-adapters/VSCodeAdapter.ts","./src/editor-adapters/EditorRegistry.ts","./src/editors/adapters/errors.ts","../../node_modules/@vitest/pretty-format/dist/index.d.ts","../../node_modules/@vitest/utils/dist/display.d.ts","../../node_modules/@vitest/utils/dist/types.d.ts","../../node_modules/@vitest/utils/dist/helpers.d.ts","../../node_modules/@vitest/utils/dist/timers.d.ts","../../node_modules/@vitest/utils/dist/index.d.ts","../../node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts","../../node_modules/@vitest/utils/dist/diff.d.ts","../../node_modules/@vitest/utils/diff.d.ts","../../node_modules/@vitest/runner/dist/tasks.d-Bh0IjN67.d.ts","../../node_modules/@vitest/runner/dist/index.d.ts","../../node_modules/vitest/dist/chunks/traces.d.402V_yFI.d.ts","../../node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts","../../node_modules/@vitest/snapshot/dist/rawSnapshot.d-D_X3-62x.d.ts","../../node_modules/@vitest/snapshot/dist/index.d.ts","../../node_modules/vitest/dist/chunks/config.d.ChUh6-ad.d.ts","../../node_modules/vitest/dist/chunks/environment.d.CrsxCzP1.d.ts","../../node_modules/vitest/dist/chunks/rpc.d.BFMWpdph.d.ts","../../node_modules/vitest/dist/chunks/worker.d.CckNUvI5.d.ts","../../node_modules/vitest/dist/chunks/browser.d.C0zGu1u9.d.ts","../../node_modules/@vitest/spy/optional-types.d.ts","../../node_modules/@vitest/spy/dist/index.d.ts","../../node_modules/tinyrainbow/dist/index.d.ts","../../node_modules/@standard-schema/spec/dist/index.d.ts","../../node_modules/@types/deep-eql/index.d.ts","../../node_modules/assertion-error/index.d.ts","../../node_modules/@types/chai/index.d.ts","../../node_modules/@vitest/expect/dist/index.d.ts","../../node_modules/@vitest/runner/dist/utils.d.ts","../../node_modules/@vitest/runner/utils.d.ts","../../node_modules/tinybench/dist/index.d.cts","../../node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts","../../node_modules/vitest/dist/chunks/global.d.D74z04P1.d.ts","../../node_modules/vitest/optional-runtime-types.d.ts","../../node_modules/@vitest/mocker/dist/types.d-BjI5eAwu.d.ts","../../node_modules/@vitest/mocker/dist/index.d-B41z0AuW.d.ts","../../node_modules/@vitest/mocker/dist/index.d.ts","../../node_modules/vitest/dist/chunks/suite.d.udJtyAgw.d.ts","../../node_modules/vitest/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts","../../node_modules/vitest/dist/runners.d.ts","../../node_modules/expect-type/dist/utils.d.ts","../../node_modules/expect-type/dist/overloads.d.ts","../../node_modules/expect-type/dist/branding.d.ts","../../node_modules/expect-type/dist/messages.d.ts","../../node_modules/expect-type/dist/index.d.ts","../../node_modules/vitest/dist/index.d.ts","../../node_modules/vitest/globals.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","../../node_modules/buffer/index.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts"],"fileIdsList":[[76,167,230,238,242,245,247,248,249,262],[167,230,238,242,245,247,248,249,262],[142,143,167,230,238,242,245,247,248,249,262],[126,139,140,141,144,167,230,238,242,245,247,248,249,262],[152,167,230,238,242,245,247,248,249,262],[152,153,167,230,238,242,245,247,248,249,262],[123,126,127,167,230,238,242,245,247,248,249,262],[123,126,167,230,238,242,245,247,248,249,262],[146,167,230,238,242,245,247,248,249,262],[123,167,230,238,242,245,247,248,249,262],[118,123,130,131,167,230,238,242,245,247,248,249,262],[118,123,130,167,230,238,242,245,247,248,249,262],[138,167,230,238,242,245,247,248,249,262],[125,167,230,238,242,245,247,248,249,262],[118,124,167,230,238,242,245,247,248,249,262],[118,167,230,238,242,245,247,248,249,262],[120,167,230,238,242,245,247,248,249,262],[118,119,120,121,122,167,230,238,242,245,247,248,249,262],[86,87,88,90,91,92,93,94,95,96,97,98,167,230,238,242,245,247,248,249,262],[81,85,86,87,167,230,238,242,245,247,248,249,262],[81,85,88,167,230,238,242,245,247,248,249,262],[91,93,94,167,230,238,242,245,247,248,249,262],[89,167,230,238,242,245,247,248,249,262],[81,85,87,88,89,167,230,238,242,245,247,248,249,262],[90,167,230,238,242,245,247,248,249,262],[86,167,230,238,242,245,247,248,249,262],[85,86,167,230,238,242,245,247,248,249,262],[85,92,167,230,238,242,245,247,248,249,262],[82,167,230,238,242,245,247,248,249,262],[82,83,84,167,230,238,242,245,247,248,249,262],[158,159,167,230,238,242,245,247,248,249,262],[158,159,160,161,167,230,238,242,245,247,248,249,262],[158,160,167,230,238,242,245,247,248,249,262],[158,167,230,238,242,245,247,248,249,262],[54,56,167,230,238,242,245,247,248,249,262],[56,167,230,238,242,245,247,248,249,262],[54,167,230,238,242,245,247,248,249,262],[52,56,78,167,230,238,242,245,247,248,249,262],[78,167,230,238,242,245,247,248,249,262],[56,78,167,230,238,242,245,247,248,249,262],[53,55,167,230,231,238,242,245,247,248,249,262],[52,56,167,230,238,242,245,247,248,249,262],[54,70,71,72,73,167,230,238,242,245,247,248,249,262],[58,69,74,75,77,167,230,238,242,245,247,248,249,262],[51,57,167,230,238,242,245,247,248,249,262],[58,69,74,167,230,238,242,245,247,248,249,262],[51,56,57,59,60,61,62,63,64,65,66,67,68,167,230,238,242,245,247,248,249,262],[99,100,101,102,167,230,238,242,245,247,248,249,262],[81,99,100,101,167,230,238,242,245,247,248,249,262],[81,100,102,167,230,238,242,245,247,248,249,262],[81,167,230,238,242,245,247,248,249,262],[128,147,148,150,167,230,238,242,245,247,248,249,262],[128,129,136,150,167,230,238,242,245,247,248,249,262],[118,126,128,132,150,167,230,238,242,245,247,248,249,262],[118,128,132,135,145,149,150,167,230,238,242,245,247,248,249,262],[128,129,132,150,167,230,238,242,245,247,248,249,262],[128,147,148,149,150,167,230,238,242,245,247,248,249,262],[128,133,134,135,150,167,230,238,242,245,247,248,249,262],[118,123,126,128,129,132,133,134,135,136,137,139,145,147,148,149,150,151,154,155,156,157,162,167,230,238,242,245,247,248,249,262],[118,126,128,129,132,133,147,148,149,150,155,167,230,238,242,245,247,248,249,262],[163,167,230,238,242,245,247,248,249,262],[167,227,228,230,238,242,245,247,248,249,262],[167,229,230,238,242,245,247,248,249,262],[230,238,242,245,247,248,249,262],[167,230,238,242,245,247,248,249,262,270],[167,230,231,236,238,241,242,245,247,248,249,251,262,267,279],[167,230,231,232,238,241,242,245,247,248,249,262],[167,230,233,238,242,245,247,248,249,262,280],[167,230,234,235,238,242,245,247,248,249,253,262],[167,230,235,238,242,245,247,248,249,262,267,276],[167,230,236,238,241,242,245,247,248,249,251,262],[167,229,230,237,238,242,245,247,248,249,262],[167,230,238,239,242,245,247,248,249,262],[167,230,238,240,241,242,245,247,248,249,262],[167,229,230,238,241,242,245,247,248,249,262],[167,230,238,241,242,243,245,247,248,249,262,267,279],[167,230,238,241,242,243,245,247,248,249,262,267,270],[167,217,230,238,241,242,244,245,247,248,249,251,262,267,279],[167,230,238,241,242,244,245,247,248,249,251,262,267,276,279],[167,230,238,242,244,245,246,247,248,249,262,267,276,279],[165,166,167,168,169,170,171,172,173,174,175,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286],[167,230,238,241,242,245,247,248,249,262],[167,230,238,242,245,247,249,262],[167,230,238,242,245,247,248,249,250,262,279],[167,230,238,241,242,245,247,248,249,251,262,267],[167,230,238,242,245,247,248,249,253,262],[167,230,238,242,245,247,248,249,254,262],[167,230,238,241,242,245,247,248,249,257,262],[167,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286],[167,230,238,242,245,247,248,249,259,262],[167,230,238,242,245,247,248,249,260,262],[167,230,235,238,242,245,247,248,249,251,262,270],[167,230,238,241,242,245,247,248,249,262,263],[167,230,238,242,245,247,248,249,262,264,280,283],[167,230,238,241,242,245,247,248,249,262,267,269,270],[167,230,238,242,245,247,248,249,262,268,270],[167,230,238,242,245,247,248,249,262,270,280],[167,230,238,242,245,247,248,249,262,271],[167,227,230,238,242,245,247,248,249,262,267,273,279],[167,230,238,242,245,247,248,249,262,267,272],[167,230,238,241,242,245,247,248,249,262,274,275],[167,230,238,242,245,247,248,249,262,274,275],[167,230,235,238,242,245,247,248,249,251,262,267,276],[167,230,238,242,245,247,248,249,262,277],[167,230,238,242,245,247,248,249,251,262,278],[167,230,238,242,244,245,247,248,249,260,262,279],[167,230,238,242,245,247,248,249,262,280,281],[167,230,235,238,242,245,247,248,249,262,281],[167,230,238,242,245,247,248,249,262,267,282],[167,230,238,242,245,247,248,249,250,262,283],[167,230,238,242,245,247,248,249,262,284],[167,230,233,238,242,245,247,248,249,262],[167,230,235,238,242,245,247,248,249,262],[167,230,238,242,245,247,248,249,262,280],[167,217,230,238,242,245,247,248,249,262],[167,230,238,242,245,247,248,249,262,279],[167,230,238,242,245,247,248,249,262,285],[167,230,238,242,245,247,248,249,257,262],[167,230,238,242,245,247,248,249,262,275],[167,217,230,238,241,242,243,245,247,248,249,257,262,267,270,279,282,283,285],[167,230,238,242,245,247,248,249,262,267,286],[167,182,185,188,189,230,238,242,245,247,248,249,262,279],[167,185,230,238,242,245,247,248,249,262,267,279],[167,185,189,230,238,242,245,247,248,249,262,279],[167,230,238,242,245,247,248,249,262,267],[167,179,230,238,242,245,247,248,249,262],[167,183,230,238,242,245,247,248,249,262],[167,181,182,185,230,238,242,245,247,248,249,262,279],[167,230,238,242,245,247,248,249,251,262,276],[167,230,238,242,245,247,248,249,262,287],[167,179,230,238,242,245,247,248,249,262,287],[167,181,185,230,238,242,245,247,248,249,251,262,279],[167,176,177,178,180,184,230,238,241,242,245,247,248,249,262,267,279],[167,185,194,202,230,238,242,245,247,248,249,262],[167,177,183,230,238,242,245,247,248,249,262],[167,185,211,212,230,238,242,245,247,248,249,262],[167,177,180,185,230,238,242,245,247,248,249,262,270,279,287],[167,185,230,238,242,245,247,248,249,262],[167,181,185,230,238,242,245,247,248,249,262,279],[167,176,230,238,242,245,247,248,249,262],[167,179,180,181,183,184,185,186,187,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,212,213,214,215,216,230,238,242,245,247,248,249,262],[167,185,204,207,230,238,242,245,247,248,249,262],[167,185,194,195,196,230,238,242,245,247,248,249,262],[167,183,185,195,197,230,238,242,245,247,248,249,262],[167,184,230,238,242,245,247,248,249,262],[167,177,179,185,230,238,242,245,247,248,249,262],[167,185,189,195,197,230,238,242,245,247,248,249,262],[167,189,230,238,242,245,247,248,249,262],[167,183,185,188,230,238,242,245,247,248,249,262,279],[167,177,181,185,194,230,238,242,245,247,248,249,262],[167,185,204,230,238,242,245,247,248,249,262],[167,197,230,238,242,245,247,248,249,262],[167,179,185,211,230,238,242,245,247,248,249,262,270,285,287],[48,49,167,230,238,242,243,245,247,248,249,262],[48,112,167,230,238,242,245,247,248,249,262],[112,113,114,115,167,230,238,242,245,247,248,249,262],[48,105,167,230,238,242,245,247,248,249,262],[105,167,230,238,242,245,247,248,249,262],[48,49,50,79,80,104,106,107,108,109,110,167,230,235,238,242,245,247,248,249,262,280,286],[48,78,167,230,238,242,245,247,248,249,262],[48,167,230,238,242,245,247,248,249,262],[49,103,167,230,238,242,245,247,248,249,262]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"471baea54ad0ef3882f9c056c31347542e575c643942048c196ef99ab01799f0","affectsGlobalScope":true,"impliedFormat":1},"96628e84d5c62b05fe8ea0921d9a7cf4cbec939300742db6e754cb5aecfba537",{"version":"ba101b4117b25580454207009f57493416d76c965094de0714dc8c3860509a61","signature":"aba9c3cc4f9d43a45729e97dc7f724874c995e9928eee24e1195461b715539dd"},{"version":"16b81141d0c59af6f07e5fc24824c54dd6003da0ab0a2d2cedc95f8eb03ea8d3","impliedFormat":1},{"version":"6578758b0b94087beffd0ce554701365cd1e6a7428f14464ac8b88095fca4e50","impliedFormat":1},{"version":"b6c4796630a47f8b0f420519cd241e8e7701247b48ed4b205e8d057cbf7107d7","impliedFormat":1},{"version":"6256cf36c8ae7e82bff606595af8fe08a06f8478140fcf304ee2f10c7716ddc8","impliedFormat":1},{"version":"b2dbe6b053e04ec135c7ce722e0a4e9744281ea40429af96e2662cc926465519","impliedFormat":1},{"version":"0646006cbfc1009e61ac6fd7c9c7fad3120e5c384638d23273e53de45edaacca","impliedFormat":1},{"version":"5619706bbd7a964d7c82cd4a307457ed0327ecc86772ceb7ea0870566c6578b2","impliedFormat":1},{"version":"b48c4e15766170c5003a6273b1d8f17f854ec565ccaaebd9f700fef159b84078","impliedFormat":1},{"version":"7c774169686976056434799723bd7a48348df9d2204b928a0b77920505585214","impliedFormat":1},{"version":"3e697e2186544103572756d80b61fcce3842ab07abdc5a1b7b8d4b9a4136005a","impliedFormat":1},{"version":"8758b438b12ea50fb8b678d29ab0ef42d77abfb801cec481596ce6002b537a6f","impliedFormat":1},{"version":"688a28e7953ef4465f68da2718dc6438aaa16325133a8cb903bf850c63cb4a7e","impliedFormat":1},{"version":"f2c96e813200ca900d2bbf0d8e3db7c2a70180b80f559b676aab8315c9472943","impliedFormat":1},{"version":"f73cf81342d2a25b65179c262ca7c38df023969129094607d0eb52510a56f10f","impliedFormat":1},{"version":"f433d28f86313073f13b16c0a18ccdd21759390f52c8d7bf9d916645b12d16ed","impliedFormat":1},{"version":"e7d7e67bd66b30f2216e4678b97bb09629a2b31766a79119acaa30e3005ef5fb","impliedFormat":1},{"version":"e05a20aa85c7324c65643542c2d7314774c2adf510f9dcbad5d3afac74ca3dac","impliedFormat":1},{"version":"e137f087bda0256410b28743ef9a1bf57a4cafd43ffa6b62d5c17a8f5a08b3b5","impliedFormat":1},{"version":"dd3e50008131e83844c49f80f7dc8e5a8fd8d1fbe0bc4da26121dda14488e507","impliedFormat":1},{"version":"af504042a6db047c40cc0aeb14550bbc954f194f2b8c5ad8944f2da502f45bf5","impliedFormat":1},{"version":"5b25b6ab5ad6c17f90b592162b2e9978ad8d81edf24cd3957306eb6e5edb89a9","impliedFormat":1},{"version":"24693bd77ac3be0b16e564d0ab498a397feb758ce7f4ed9f13478d566e3aafde","impliedFormat":1},{"version":"208dad548b895c7d02465de6ba79064b7c67bc4d94e5227b09f21d58790e634c","impliedFormat":1},{"version":"048c0ced65fa41fbf4bcc3d5e8e5b6f6c7f27335ceb54d401be654e821adbc08","impliedFormat":1},{"version":"e1126668c194faa56a728eb5bd2dd88dfc19460ced65c38888977a22369e4624","impliedFormat":1},{"version":"1df8dcaa6969c836cfe30ae6c0b91d2d94c8706be13ac55d203a639741908a76","impliedFormat":1},{"version":"e84e587a2392736f944496a5805c787473fd3edc5a5d8046c0063b2334c05fcc","impliedFormat":1},{"version":"fe54919026a7e4762fc03f58baa8a2bf856af8de0eaae0a0d27265fa122565e5","impliedFormat":1},{"version":"b5ac30f33e1f72de445ad09a966d349bb755c06a8b583d3fb350259de07123bf","signature":"ca0fb8bb51cb98487feade895431b8e6fe32db6e54934e2a4e317b67d238ac59"},{"version":"ae413c7d04845bec12059d0b5349f3e5fb8769a104591ff0912d48495e318ea4","signature":"b40f0bcde2c563510b5ed092d5759d75c446aca764e270f0b24c5b353ab2391c"},{"version":"14ecfc29e0c44ad4c5e50f9b597492cd8f45a2a635db8b5fe911a5da83e26cf8","impliedFormat":1},{"version":"569e762cf47aafdad508360a443c6c757e56c61db3b652b65458a7d168d139c4","impliedFormat":99},{"version":"02ed2766d79a00719ac3cc77851d54bd7197c1b12085ea12126bc2a65068223e","impliedFormat":99},{"version":"4b84373e192b7e0f8569b65eb16857098a6ee279b75d49223db2a751fdd7efde","impliedFormat":99},{"version":"5aeea312cd1d3cc5d72fc8a9c964439d771bdf41d9cce46667471b896b997473","impliedFormat":99},{"version":"1d963927f62a0d266874e19fcecf43a7c4f68487864a2c52f51fbdd7c5cc40d8","impliedFormat":99},{"version":"d7341559b385e668ca553f65003ccc5808d33a475c141798ba841992fef7c056","impliedFormat":99},{"version":"fcf502cbb816413ab8c79176938357992e95c7e0af3aa2ef835136f88f5ad995","impliedFormat":99},{"version":"5c59fd485fff665a639e97e9691a7169f069e24b42ffc1f70442c55720ad3969","impliedFormat":99},{"version":"89c6bcc4f7b19580009a50674b4da0951165c8a2202fa908735ccbe35a5090dd","impliedFormat":99},{"version":"df283af30056ef4ab9cf31350d4b40c0ed15b1032833e32dc974ade50c13f621","impliedFormat":99},{"version":"9de40cf702d52a49d6f3d36d054fc12638348ea3e1fb5f8d53ef8910e7eaa56f","impliedFormat":99},{"version":"2f844dc2e5d3e8d15a951ff3dc39c7900736d8b2be67cc21831b50e5faaa760a","impliedFormat":99},{"version":"ecbbfd67f08f18500f2faaaa5d257d5a81421e5c0d41fa497061d2870b2e39db","impliedFormat":99},{"version":"79570f4dfd82e9ae41401b22922965da128512d31790050f0eaf8bbdb7be9465","impliedFormat":99},{"version":"4b7716182d0d0349a953d1ff31ab535274c63cbb556e88d888caeb5c5602bc65","impliedFormat":99},{"version":"d51809d133c78da34a13a1b4267e29afb0d979f50acbeb4321e10d74380beeea","impliedFormat":99},{"version":"e1dafdb1db7e8b597fc0dbc9e4ea002c39b3c471be1c4439eda14cf0550afe92","impliedFormat":99},{"version":"6ea4f73a90f9914608bd1ab342ecfc67df235ad66089b21f0632264bb786a98e","impliedFormat":99},{"version":"ba0badf6398010ef0ba77947d4b52078fc84cb4f209f6b70194b3b2cfb1823d8","impliedFormat":99},{"version":"dd018ed60101a59a8e89374e62ed5ab3cb5df76640fc0ab215c9adf8fbc3c4b0","impliedFormat":99},{"version":"8d401f73380bdd30293e1923338e2544d57a9cdbd3dd34b6d24df93be866906e","impliedFormat":99},{"version":"6a33d9e50fc28d0a7431e29fd7a07d7a74ac0218c6c17f9fecbed52a1985ecb5","impliedFormat":99},{"version":"c8894d023821e1e94c753e118d995dea2551b998d2a87a35f701684ce30ec3a4","signature":"3a02d67e8e78f48a03a675acf0e23f75327f7357bf36f7a31b75325806d15227"},{"version":"d7b12257a785f4d7c4bdab32e8d5a431adcde9b18d115d5d7361ab4012ab2c54","signature":"1558b49afdec2051f6742faff973845ad3eee7b2cf6042cb1b806ce946e7eb96"},{"version":"cea4a505348012bdb8f0e269674a818464abbb99a6a7dd505b0f6f66ea3878d8","signature":"6f979a314192657d1e43b006ba2dea68c5e5f5466d143eb8187484ded2f36ddc"},{"version":"742f4c947e11e021cfd2b18e5da1e14fb002ff9629aa7d877c27b30f4028786a","signature":"b1c92890355636a3f01df85cd52e61c82fa4306914ddb553a99b41c231d4cfce"},{"version":"d4b35fc08a78cc4b4a1e49e767f1cfebe0b86ff5e252ce01888ceb142f19b0a3","signature":"12d76b9de3e454a26d03346dddd1f2691edbf1740856f6c37f3821d7aad401e1"},{"version":"6d005f6ff36016ae97d7a5a9818dfd86f5eebeba978916518bcdfe5ef8dd16ac","signature":"46a300915669771a6e74336efbe905fa1cda99e8aa3bb7e6977f85e0abdac7dc"},{"version":"ac4ebc1a724c6eb7ba741da87970e603fe66c887498a00e81614ab57f9f05a4b","signature":"6d152a11ee0377a8290eb14df5855874d9ce7065687c368aad890916f9b4152e"},{"version":"b3bf117491c383ad81c9803e91296a72452e64408f289c28b9f3734fa7e699df","signature":"e888f57c70a5655dedf190409bbc1dd992ff56f48b2f4cea147e43ca9258dcf2"},{"version":"39fa0fc4a237d6cd97ec66a4fb270773188b523ef44889edf822070dfbdcbed1","signature":"6cbc075bb1da0379d3a788745c4a4aa2be96a9be6ef13eab7832539b4107b021"},{"version":"493965e688659eb8867dfd2aeb5f699be280f473061e7a83aca6584fd3efeb9b","signature":"9e9ced2e4500765be59a4b0ac15cf9e3d8cdd0cc22d4ce8d95cb4f3b6fb4ad6f"},{"version":"537364b1663e2cef6fd873bd5fa9b8c19bc39e0d65d523bae04bbdac28c107a6","signature":"6c13901971fd1cd897e21a711fccbfac56768edc98c92935ad6c215232f95664"},{"version":"94ba1878a2f2de015eb831f76fafc56c3ae36e06fb023a19536b9788b2752d0d","signature":"f717c7bc0b6cfaaf7725c9f50f47cbdf38702ea74fa928efea244dfbd317a507"},{"version":"2d2fe46f1730ca0f02d84ce0e84b776af7a4e0f4fc90f1671e40c3ba155251dc","signature":"490579719064292e1bfaf4b20f140c1cbe1b2e75e148c0006fe53232e0540031"},{"version":"cf9b40e2006b0fe615afa6103709f1e715a91001732b9786938794a22cc7abcb","signature":"10da41c7ca88f84ac925c92fcea70e6ef0b53a6ef586bdbee0dd238bfc347f0b"},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"3b0a56d056d81a011e484b9c05d5e430711aaecd561a788bad1d0498aad782c7","impliedFormat":99},{"version":"05c7aef6a4e496b93c2e682cced8903c0dfe6340d04f3fe616176e2782193435","impliedFormat":99},{"version":"9443967db823b66d1682be7fc66392be7c7924e10c3e54900f456341e94591a6","impliedFormat":99},{"version":"424f71d1fae96ac2e878af92345bb87bea1d29f757228fbc190133b305643f2c","impliedFormat":99},{"version":"ac3d263474022e9a14c43f588f485d549641d839b159ecc971978b90f34bdf6b","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"6bb6d57454370324434bcf355942dee45b0e0d8ab0fa3e98bafe8a30718273b4","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"a86701e56b10a6d1ef9b2ecaeedbab94ed7b957a646cd71fd09d02b323c6d3d7","impliedFormat":99},{"version":"976932e3807786cdae46ed5dfcd02c44f3fa25c157a0e8392f5a2dabb9a14a4e","impliedFormat":99},{"version":"59b7a8ec1781284f6602af48487b68fc3baadf34cb4cbcbb31f213b6712fac34","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"06db2f8ba1d1dfacf04529cb731081ab23f133f29c7608ebdfbcab356996827c","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"ed9bb55ddcbebd5cb3eee991f57ff21438546ee40ee1c310281bd12a6c7cf65b","impliedFormat":99},{"version":"e666e31d323fef5642f87db0da48a83e58f0aaf9e3823e87eabd8ec7e0441a36","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":1},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"4c3d12ac5744ff4ba2e1ce97ec307f09d726b4cfcfd5eff3315ccc080d620fb9","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"abdaf8c2f20089a6b23a6287007ed16f9cf76d0045ce2973a5f8508c87286d21","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"8c9917efcdf61e9b9a73ac1e289c612f12db33519ca1445cca41865f7887c737","impliedFormat":99},{"version":"8d7cbeea0454e05a3cdf3370c5df267072c4f1dc6c48a45a9ad750d7890443d7","affectsGlobalScope":true,"impliedFormat":99},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"074de5b2fdead0165a2757e3aaef20f27a6347b1c36adea27d51456795b37682","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"24371e69a38fc33e268d4a8716dbcda430d6c2c414a99ff9669239c4b8f40dea","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"3e11fce78ad8c0e1d1db4ba5f0652285509be3acdd519529bc8fcef85f7dafd9","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"c63b9ada8c72f95aac5db92aea07e5e87ec810353cdf63b2d78f49a58662cf6c","impliedFormat":1},{"version":"1cc2a09e1a61a5222d4174ab358a9f9de5e906afe79dbf7363d871a7edda3955","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"b64d4d1c5f877f9c666e98e833f0205edb9384acc46e98a1fef344f64d6aba44","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"c9381908473a1c92cb8c516b184e75f4d226dad95c3a85a5af35f670064d9a2f","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fad5618174d74a34ee006406d4eb37e8d07dd62eb1315dbf52f48d31a337547","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b50a819485ffe0d237bf0d131e92178d14d11e2aa873d73615a9ec578b341f5","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"1d024184fb57c58c5c91823f9d10b4915a4867b7934e89115fd0d861a9df27c8","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"12d602a8fe4c2f2ba4f7804f5eda8ba07e0c83bf5cf0cda8baffa2e9967bfb77","affectsGlobalScope":true,"impliedFormat":1},{"version":"a856ab781967b62b288dfd85b860bef0e62f005ed4b1b8fa25c53ce17856acaf","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"8db46b61a690f15b245cf16270db044dc047dce9f93b103a59f50262f677ea1f","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"757227c8b345c57d76f7f0e3bbad7a91ffca23f1b2547cbed9e10025816c9cb7","impliedFormat":1},{"version":"959d0327c96dd9bb5521f3ed6af0c435996504cc8dd46baa8e12cb3b3518cef1","impliedFormat":1},{"version":"e1c1a0b4d1ead0de9eca52203aeb1f771f21e6238d6fcd15aa56ac2a02f1b7bf","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"266bee0a41e9c3ba335583e21e9277ae03822402cf5e8e1d99f5196853613b98","affectsGlobalScope":true,"impliedFormat":1},{"version":"ee96415bb64198cc13555da26474825a638e48e5a3c03cb33dd82b7e68fcc417","impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"e236b5eba291f51bdf32c231673e6cab81b5410850e61f51a7a524dddadc0f95","impliedFormat":1},{"version":"ce8653341224f8b45ff46d2a06f2cacb96f841f768a886c9d8dd8ec0878b11bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"90a278f5fab7557e69e97056c0841adf269c42697194f0bd5c5e69152637d4b3","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"f263485c9ca90df9fe7bb3a906db9701997dc6cae86ace1f8106ac8d2f7f677b","impliedFormat":1},{"version":"4c64e7fa79f96cac57f4e22899805f88d22d69ac673b63fe4fa14e5229200bba","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"eab2f3179607acb3d44b2db2a76dd7d621c5039b145dc160a1ee733963f9d2f5","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"1dbca38aa4b0db1f4f9e6edacc2780af7e028b733d2a98dd3598cd235ca0c97d","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"19143c930aef7ccf248549f3e78992f2f1049118ec5d4622e95025057d8e392b","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"cca8917838a876e2d7016c9b6af57cbf11fdf903c5fdd8e613fa31840b2957bf","impliedFormat":1},{"version":"d91ae55e4282c22b9c21bc26bd3ef637d3fe132507b10529ae68bf76f5de785b","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"7e8a671604329e178bb479c8f387715ebd40a091fc4a7552a0a75c2f3a21c65c","impliedFormat":1},{"version":"41ef7992c555671a8fe54db302788adefa191ded810a50329b79d20a6772d14c","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"4c5e90ddbcd177ad3f2ffc909ae217c87820f1e968f6959e4b6ba38a8cec935e","impliedFormat":1},{"version":"b70dd9a44e1ac42f030bb12e7d79117eac7cb74170d72d381a1e7913320af23a","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1}],"root":[50,79,80,[104,117]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":1,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":7},"referencedMap":[[77,1],[76,2],[81,2],[141,2],[144,3],[142,2],[48,2],[145,4],[153,5],[154,6],[152,2],[118,2],[128,7],[127,8],[146,7],[147,9],[130,10],[132,11],[131,12],[139,13],[138,2],[126,14],[125,15],[119,16],[121,17],[123,18],[122,2],[124,16],[120,2],[143,2],[252,2],[97,2],[87,2],[99,19],[88,20],[86,21],[95,22],[98,23],[90,24],[91,25],[89,26],[92,27],[93,28],[94,27],[96,2],[82,2],[84,29],[83,29],[85,30],[160,31],[162,32],[161,33],[159,34],[158,2],[70,35],[54,36],[71,35],[72,37],[73,37],[59,2],[60,36],[61,36],[62,38],[63,38],[64,39],[65,39],[51,2],[66,39],[57,40],[67,36],[52,36],[68,39],[55,37],[56,41],[53,42],[74,43],[78,44],[58,45],[75,46],[69,47],[103,48],[102,49],[101,50],[100,51],[148,2],[140,2],[46,2],[47,2],[9,2],[8,2],[2,2],[10,2],[11,2],[12,2],[13,2],[14,2],[15,2],[16,2],[17,2],[3,2],[18,2],[19,2],[4,2],[20,2],[24,2],[21,2],[22,2],[23,2],[25,2],[26,2],[27,2],[5,2],[28,2],[29,2],[30,2],[31,2],[6,2],[35,2],[32,2],[33,2],[34,2],[36,2],[7,2],[37,2],[42,2],[43,2],[38,2],[39,2],[40,2],[41,2],[1,2],[44,2],[45,2],[149,52],[137,53],[133,54],[134,10],[156,2],[150,55],[135,56],[155,57],[129,2],[136,58],[163,59],[157,60],[164,61],[151,2],[49,2],[227,62],[228,62],[229,63],[167,64],[230,65],[231,66],[232,67],[165,2],[233,68],[234,69],[235,70],[236,71],[237,72],[238,73],[239,73],[240,74],[241,75],[242,76],[243,77],[168,2],[166,2],[244,78],[245,79],[246,80],[287,81],[247,82],[248,83],[249,82],[250,84],[251,85],[253,86],[254,87],[255,87],[256,87],[257,88],[258,89],[259,90],[260,91],[261,92],[262,93],[263,93],[264,94],[265,2],[266,2],[267,95],[268,96],[269,95],[270,97],[271,98],[272,99],[273,100],[274,101],[275,102],[276,103],[277,104],[278,105],[279,106],[280,107],[281,108],[282,109],[283,110],[284,111],[169,82],[170,2],[171,112],[172,113],[173,2],[174,114],[175,2],[218,115],[219,116],[220,117],[221,117],[222,118],[223,2],[224,65],[225,119],[226,116],[285,120],[286,121],[194,122],[206,123],[191,124],[207,125],[216,126],[182,127],[183,128],[181,129],[215,130],[210,131],[214,132],[185,133],[203,134],[184,135],[213,136],[179,137],[180,131],[186,138],[187,2],[193,139],[190,138],[177,140],[217,141],[208,142],[197,143],[196,138],[198,144],[201,145],[195,146],[199,147],[211,130],[188,148],[189,149],[202,150],[178,125],[205,151],[204,138],[192,149],[200,152],[209,2],[176,2],[212,153],[80,154],[113,155],[112,2],[116,156],[114,155],[115,155],[110,157],[107,157],[109,157],[106,158],[108,157],[117,157],[105,2],[111,159],[79,160],[50,161],[104,162]],"latestChangedDtsFile":"./dist/websocket/WebSocketClient.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/scripts/.gitkeep b/scripts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/README.md b/scripts/README.md index ba5f403..9cb227f 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,183 +1,183 @@ -# Testing Scripts +# Development, Quality Control & Orchestration Scripts -This directory contains utility scripts for manual testing of the Git Integration & File Diffing feature. +Following the **CodeLink V2 Architecture Rewrite**, all manual testing shell scripts have been deprecated and replaced with: -## Available Scripts +1. **Docker Compose Infrastructure Orchestration** for full-stack local services. +2. **Monorepo-wide NPM Workspace Scripts** for unified TypeScript builds, linting, formatting, and unit tests. +3. **Native Go Tooling** for robust testing and execution of backend microservices. -### 1. `generate-test-files.sh` +This guide provides a comprehensive reference of available quality control scripts, commands, and development workflows. -Generates test files for manual testing scenarios. +--- -**Usage:** +## πŸ› οΈ System Prerequisites -```bash -./scripts/generate-test-files.sh -``` +To run the local validation and orchestration scripts, ensure the following are installed: + +- **Node.js** (v20.x or higher) +- **npm** (v9.x or higher) +- **Go** (v1.21 or higher) +- **Docker & Docker Compose** -**Generated Files:** +--- + +## πŸŽ›οΈ Unified NPM Workspace Scripts -- `test-untracked.ts` - Untracked TypeScript file -- `test-large.ts` - Large file with 10,500 lines -- `test-binary.bin` - Binary file for testing -- `test-config.json` - JSON configuration file -- `test-documentation.md` - Markdown documentation file +The root `package.json` defines monorepo-wide scripts that coordinate tasks across all TypeScript workspaces (`packages/protocol`, `packages/vscode-extension`, and `packages/mobile`) and Go services (`services/auth`, `services/relay`). -**When to use:** +### Script Directory Reference -- Before starting manual testing -- To quickly set up test scenarios -- To verify system handles different file types +| Command | Action | Scope / Under the Hood | +| :------------------------ | :------------------------- | :-------------------------------------------------------------------------------- | +| `npm run build` | Builds all workspaces | Runs `npm run build` in all workspaces containing it. | +| `npm run lint` | Lints entire codebase | Runs ESLint across all `.ts` and `.tsx` files (ignoring `.eslintignore`). | +| `npm run lint:fix` | Fixes lint issues | Runs ESLint with the `--fix` flag on all TypeScript files. | +| `npm run format` | Prettifies codebase | Re-formats typescript, javascript, JSON, and markdown files using Prettier. | +| `npm run format:check` | Verifies formatting | Validates that files conform to Prettier styling guidelines. | +| `npm run typecheck` | Validates TypeScript types | Runs typecheck sequentially across `protocol`, `vscode`, and `mobile` workspaces. | +| `npm run test` | Runs JS unit tests | Executes all Vitest unit tests in a single headless run. | +| `npm run test:watch` | Watch-mode JS tests | Launches the Vitest test runner in interactive watch mode. | +| `npm run test:coverage` | Generates JS coverage | Runs Vitest tests and outputs a V8 coverage report to `coverage/`. | +| `npm run test:go` | Runs Go unit tests | Executes unit tests across all Go microservices (`go test ./services/...`). | +| `npm run test:go:verbose` | Verbose Go testing | Runs Go unit tests with detailed output (`go test -v ./services/...`). | +| `npm run precommit` | Pre-commit sanity check | Sequentially runs linting, typechecking, and format checks. | --- -### 2. `cleanup-test-files.sh` +## 🐳 Backend Service Orchestration (`infra/`) -Removes all test files generated by `generate-test-files.sh`. +The core Go services, Postgres database, and Redis cache are orchestratable via Docker Compose located in the `/infra` directory. -**Usage:** +### Quick Start Full Backend Services + +From the project root, run: ```bash -./scripts/cleanup-test-files.sh +# Build and start all backend services (Postgres, Redis, Auth Service, Relay Service) +docker compose -f infra/docker-compose.yml up --build ``` -**Removed Files:** - -- All `test-*.ts` files -- All `test-*.json` files -- All `test-*.md` files -- All `test-*.bin` files -- All `test-*.png` files +### Local Dev Port Map -**When to use:** +When spun up, the orchestration layer exposes the following ports on the host system: -- After completing manual testing -- To clean up workspace -- Before committing changes +- **Auth Service**: `8081` (REST API) +- **Relay Service**: `8082` (WebSocket Hub) +- **Postgres Database**: `5432` (Relational Storage) +- **Redis Cache**: `6379` (In-memory PubSub & Token Caching) --- -### 3. `verify-manual-test-setup.sh` +## πŸ“¦ Component-Specific Development & Scripts -Verifies that the manual testing environment is properly configured. +### 1. Shared Protocol (`packages/protocol`) -**Usage:** +The protocol package defines typescript contracts and message codecs shared between the extension and mobile clients. -```bash -./scripts/verify-manual-test-setup.sh -``` +- **Build Output**: Compiles into `dist/` +- **Commands**: + ```bash + cd packages/protocol + npm run build # Compile TypeScript + npm run dev # Watch mode for changes + npm run test # Run protocol unit tests + ``` -**Checks:** +### 2. VS Code Extension (`packages/vscode-extension`) -- βœ“ Documentation files exist -- βœ“ Script files exist and are executable -- βœ“ Package directories exist -- βœ“ Build outputs exist -- βœ“ Source files exist -- βœ“ VS Code configuration exists -- βœ“ Required commands available (node, npm, git) -- βœ“ Node.js version >= 20.x -- βœ“ Packages are built -- βœ“ Git repository initialized +Integrates local editors (Continue, Kiro, Cursor, Antigravity) with Git diff streams and WebSocket commands. -**Exit Codes:** +- **Build Output**: Compiles extension assets into `dist/extension.js` +- **Commands**: + ```bash + cd packages/vscode-extension + npm run build # Compile Extension + npm run dev # Watch mode (F5 inside VS Code to launch extension debugger host) + npm run test # Run extension unit tests + ``` -- `0` - All checks passed or only warnings -- `1` - Errors found, manual testing not ready +### 3. Mobile Client (`packages/mobile`) -**When to use:** +React Native & Expo PWA client featuring QR pairing, live unified diff views, and AI prompt compositions. -- Before starting manual testing -- After building packages -- To troubleshoot setup issues - ---- +- **Commands**: + ```bash + cd packages/mobile + npm start # Start Expo developer tool + npm run android # Run on connected Android device/emulator + npm run ios # Run on connected iOS device/simulator + npm run web # Run mobile client inside your web browser + ``` -## Quick Workflow +### 4. Go Microservices (`services/auth` & `services/relay`) -### Initial Setup +Performance-critical components managing cryptographical keys, session lifecycles, and high-frequency WebSocket relays. -```bash -# 1. Verify setup -./scripts/verify-manual-test-setup.sh +- **Commands**: -# 2. Generate test files -./scripts/generate-test-files.sh -``` + ```bash + # Run unit tests across both services from root + npm run test:go -### After Testing + # Run verbose tests from root + npm run test:go:verbose -```bash -# Clean up test files -./scripts/cleanup-test-files.sh -``` + # Or run tests directly inside a specific service directory: + cd services/auth && go test -v ./... + cd services/relay && go test -v ./... + ``` --- -## Script Permissions +## πŸ›‘οΈ Code Quality & PR Readiness -All scripts should be executable. If not, run: +To prevent syntax, type, or lint failures in the CI/CD pipeline, run the root pre-commit suite prior to executing git commits: ```bash -chmod +x scripts/*.sh +# Validates code style, structural types, and formatting +npm run precommit ``` ---- - -## Troubleshooting - -### "Permission denied" error +If any format errors are flagged, quickly resolve them by running: ```bash -# Make scripts executable -chmod +x scripts/*.sh +npm run format +npm run lint:fix ``` -### "Command not found" error +--- -```bash -# Ensure you're in the project root directory -cd /path/to/CodeLink +## πŸ“ Troubleshooting & Common Development Issues -# Run with explicit bash -bash scripts/generate-test-files.sh -``` +### 1. `vitest: command not found` -### Scripts don't work on Windows +Ensure you have run clean package installation at the root level using the standard legacy flags if needed: ```bash -# Use Git Bash or WSL -# Or run commands manually from the script +npm install ``` ---- - -## Manual Testing Documentation +### 2. Go test failures or dependencies missing -For complete manual testing procedures, see: +Make sure you are inside a Go workspace setup. Check the `go.work` configuration in the root: -- **QUICK_TEST_REFERENCE.md** - Quick reference card -- **MANUAL_TEST_EXECUTION.md** - Streamlined testing guide -- **MANUAL_TEST_CHECKLIST.md** - Detailed checklist -- **MANUAL_TESTING_GUIDE.md** - Comprehensive guide -- **MANUAL_TEST_SUMMARY.md** - Overview and status +```go +go 1.26.3 ---- - -## Contributing +use ( + ./services/auth + ./services/relay +) +``` -When adding new test scripts: +Ensure your Go environment is configured correctly, and run `go mod tidy` in the relevant service directories if dependencies are out of sync. -1. Make them executable: `chmod +x scripts/new-script.sh` -2. Add error handling: `set -e` -3. Add helpful output with colors -4. Document in this README -5. Update verification script if needed +### 3. Docker port conflicts ---- +If `docker compose` fails to bind to ports `8081`, `8082`, `5432`, or `6379`, verify that no local PostgreSQL, Redis, or node servers are already active on your host system: -## Notes - -- Scripts are designed for Unix-like systems (Linux, macOS) -- Windows users should use Git Bash or WSL -- All scripts assume they're run from the project root -- Test files are intentionally not tracked by Git +```bash +# Check if services are already using these ports +sudo lsof -i :5432 +sudo lsof -i :6379 +``` diff --git a/scripts/cleanup-test-files.sh b/scripts/cleanup-test-files.sh deleted file mode 100755 index 8a229d4..0000000 --- a/scripts/cleanup-test-files.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# Script to clean up test files after manual testing -# Usage: ./scripts/cleanup-test-files.sh - -set -e - -echo "🧹 Cleaning up test files..." - -# List of test files to remove -TEST_FILES=( - "test-untracked.ts" - "test-added.ts" - "test-large.ts" - "test-binary.bin" - "test-image.png" - "test-config.json" - "test-documentation.md" -) - -REMOVED_COUNT=0 - -for file in "${TEST_FILES[@]}"; do - if [ -f "$file" ]; then - rm "$file" - echo "βœ“ Removed $file" - REMOVED_COUNT=$((REMOVED_COUNT + 1)) - fi -done - -# Also remove from Git if they were added -echo "" -echo "πŸ” Checking Git status..." -git status --short | grep "^??" | awk '{print $2}' | while read -r file; do - if [[ "$file" == test-* ]]; then - echo " Found untracked test file: $file" - fi -done - -echo "" -if [ $REMOVED_COUNT -eq 0 ]; then - echo "βœ… No test files found to clean up" -else - echo "βœ… Cleaned up $REMOVED_COUNT test file(s)" -fi - -echo "" -echo "πŸ’‘ If you committed any test files, run:" -echo " git reset HEAD test-*.ts test-*.json test-*.md test-*.bin" -echo " git checkout -- test-*.ts test-*.json test-*.md test-*.bin" diff --git a/scripts/generate-test-files.sh b/scripts/generate-test-files.sh deleted file mode 100755 index 6827866..0000000 --- a/scripts/generate-test-files.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash - -# Script to generate test files for manual testing -# Usage: ./scripts/generate-test-files.sh - -set -e - -echo "πŸ§ͺ Generating test files for manual testing..." - -# Test 1: Untracked TypeScript file -echo "πŸ“ Creating untracked TypeScript file..." -cat > test-untracked.ts << 'EOF' -// This is an untracked file for testing -export const untrackedConstant = 'This file is not in Git'; - -export function untrackedFunction() { - console.log('Testing untracked file handling'); - return true; -} -EOF -echo "βœ“ Created test-untracked.ts" - -# Test 2: Large file (10,500 lines) -echo "πŸ“ Creating large TypeScript file (10,500 lines)..." -node -e " -const lines = []; -lines.push('// Large file for performance testing'); -lines.push('// This file has 10,500 lines'); -lines.push(''); -for (let i = 0; i < 10500; i++) { - lines.push(\`export const variable\${i} = \${i}; // Line \${i}\`); -} -require('fs').writeFileSync('test-large.ts', lines.join('\n')); -" -echo "βœ“ Created test-large.ts (10,500 lines)" - -# Test 3: Binary file (dummy PNG header) -echo "πŸ“ Creating binary test file..." -printf '\x89PNG\x0D\x0A\x1A\x0A\x00\x00\x00\x0DIHDR' > test-binary.bin -echo "βœ“ Created test-binary.bin" - -# Test 4: JSON test file -echo "πŸ“ Creating JSON test file..." -cat > test-config.json << 'EOF' -{ - "name": "manual-test-config", - "version": "1.0.0", - "description": "Test JSON file for manual testing", - "settings": { - "enabled": true, - "timeout": 5000, - "retries": 3 - }, - "features": [ - "git-integration", - "diff-viewing", - "websocket-communication" - ] -} -EOF -echo "βœ“ Created test-config.json" - -# Test 5: Markdown test file -echo "πŸ“ Creating Markdown test file..." -cat > test-documentation.md << 'EOF' -# Manual Testing Documentation - -This is a test markdown file for manual testing. - -## Features to Test - -- File change detection -- Git integration -- Diff generation -- WebSocket communication - -## Test Scenarios - -1. Edit this file -2. Wait for debounce (1 second) -3. Check mobile client for diff -4. Verify orange dot appears (isDirty) -5. Save file and verify orange dot disappears - -### Expected Results - -The diff should appear on the mobile client within 1-2 seconds. - -**Performance metrics:** -- Git operation: < 500ms -- Diff generation: < 200ms -- Total pipeline: < 2000ms -EOF -echo "βœ“ Created test-documentation.md" - -echo "" -echo "βœ… Test files generated successfully!" -echo "" -echo "πŸ“‹ Generated files:" -echo " - test-untracked.ts (untracked file)" -echo " - test-large.ts (10,500 lines)" -echo " - test-binary.bin (binary file)" -echo " - test-config.json (JSON file)" -echo " - test-documentation.md (Markdown file)" -echo "" -echo "πŸ”§ Next steps:" -echo " 1. Start relay server: cd packages/relay-server && npm start" -echo " 2. Start mobile client: cd packages/mobile-client && npm run dev" -echo " 3. Press F5 in VS Code to launch Extension Development Host" -echo " 4. Open test files in Extension Development Host and make changes" -echo "" -echo "🧹 To clean up test files later, run:" -echo " ./scripts/cleanup-test-files.sh" diff --git a/scripts/setup-git-hooks.sh b/scripts/setup-git-hooks.sh index e22abfd..749f3a8 100755 --- a/scripts/setup-git-hooks.sh +++ b/scripts/setup-git-hooks.sh @@ -1,36 +1,44 @@ #!/bin/bash -# Setup script for Git hooks -# Run this script to install pre-commit hooks for the CodeLink project -set -e +# Ensure we are in the repository root +cd "$(dirname "$0")/.." -echo "πŸ”§ Setting up Git hooks for CodeLink..." -echo "" +# Determine the correct git hooks directory (especially in worktrees) +HOOKS_DIR=$(git rev-parse --git-path hooks 2>/dev/null) -# Check if .git directory exists -if [ ! -d ".git" ]; then - echo "❌ Error: .git directory not found. Are you in the repository root?" - exit 1 +if [ -z "$HOOKS_DIR" ]; then + # Fallback to standard .git/hooks if git command fails + HOOKS_DIR=".git/hooks" fi +HOOK_FILE="$HOOKS_DIR/pre-commit" + +echo "Setting up CodeLink pre-commit git hook in $HOOKS_DIR..." + # Create hooks directory if it doesn't exist -mkdir -p .git/hooks - -# Copy pre-commit hook -echo "πŸ“‹ Installing pre-commit hook..." -cp .github/pre-commit-hook.sh .git/hooks/pre-commit -chmod +x .git/hooks/pre-commit -echo "βœ… Pre-commit hook installed" -echo "" - -echo "✨ Git hooks setup complete!" -echo "" -echo "The pre-commit hook will now run automatically before each commit." -echo "It will check:" -echo " - ESLint (code quality)" -echo " - TypeScript compilation (type safety)" -echo " - Prettier formatting (code style)" -echo "" -echo "To bypass the hook (not recommended), use: git commit --no-verify" -echo "" -echo "To manually run pre-commit checks: npm run precommit" +mkdir -p "$HOOKS_DIR" + +# Write the hook script +cat << 'EOF' > "$HOOK_FILE" +#!/bin/bash + +echo "Running pre-commit hooks (lint, typecheck, format check)..." + +# Run precommit command +npm run precommit + +RESULT=$? + +if [ $RESULT -ne 0 ]; then + echo "❌ Pre-commit checks failed! Please fix the errors before committing." + exit 1 +fi + +echo "βœ… Pre-commit checks passed!" +exit 0 +EOF + +# Make it executable +chmod +x "$HOOK_FILE" + +echo "βœ… Pre-commit git hook successfully installed!" diff --git a/scripts/verify-manual-test-setup.sh b/scripts/verify-manual-test-setup.sh deleted file mode 100755 index 7dc4eb6..0000000 --- a/scripts/verify-manual-test-setup.sh +++ /dev/null @@ -1,220 +0,0 @@ -#!/bin/bash - -# Script to verify manual testing setup is complete -# Usage: ./scripts/verify-manual-test-setup.sh - -set -e - -echo "πŸ” Verifying manual testing setup..." -echo "" - -# Color codes -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -ERRORS=0 -WARNINGS=0 - -# Function to check file exists -check_file() { - if [ -f "$1" ]; then - echo -e "${GREEN}βœ“${NC} $1" - else - echo -e "${RED}βœ—${NC} $1 (missing)" - ERRORS=$((ERRORS + 1)) - fi -} - -# Function to check directory exists -check_dir() { - if [ -d "$1" ]; then - echo -e "${GREEN}βœ“${NC} $1/" - else - echo -e "${RED}βœ—${NC} $1/ (missing)" - ERRORS=$((ERRORS + 1)) - fi -} - -# Function to check command exists -check_command() { - if command -v "$1" &> /dev/null; then - VERSION=$($1 --version 2>&1 | head -n 1) - echo -e "${GREEN}βœ“${NC} $1 ($VERSION)" - else - echo -e "${RED}βœ—${NC} $1 (not installed)" - ERRORS=$((ERRORS + 1)) - fi -} - -# Check documentation files -echo "πŸ“š Checking documentation files..." -check_file "MANUAL_TESTING_GUIDE.md" -check_file "MANUAL_TEST_CHECKLIST.md" -check_file "MANUAL_TEST_EXECUTION.md" -check_file "MANUAL_TEST_SUMMARY.md" -echo "" - -# Check script files -echo "πŸ”§ Checking script files..." -check_file "scripts/generate-test-files.sh" -check_file "scripts/cleanup-test-files.sh" -check_file "scripts/verify-manual-test-setup.sh" - -# Check if scripts are executable -if [ -x "scripts/generate-test-files.sh" ]; then - echo -e "${GREEN}βœ“${NC} generate-test-files.sh is executable" -else - echo -e "${YELLOW}⚠${NC} generate-test-files.sh is not executable (run: chmod +x scripts/*.sh)" - WARNINGS=$((WARNINGS + 1)) -fi - -if [ -x "scripts/cleanup-test-files.sh" ]; then - echo -e "${GREEN}βœ“${NC} cleanup-test-files.sh is executable" -else - echo -e "${YELLOW}⚠${NC} cleanup-test-files.sh is not executable (run: chmod +x scripts/*.sh)" - WARNINGS=$((WARNINGS + 1)) -fi -echo "" - -# Check package directories -echo "πŸ“¦ Checking package directories..." -check_dir "packages/protocol" -check_dir "packages/relay-server" -check_dir "packages/vscode-extension" -check_dir "packages/mobile-client" -echo "" - -# Check build outputs -echo "πŸ—οΈ Checking build outputs..." -check_dir "packages/protocol/dist" -check_dir "packages/relay-server/dist" -check_dir "packages/vscode-extension/dist" -check_dir "packages/mobile-client/dist" -echo "" - -# Check key source files -echo "πŸ“ Checking key source files..." -check_file "packages/vscode-extension/src/extension.ts" -check_file "packages/vscode-extension/src/watcher/FileWatcher.ts" -check_file "packages/vscode-extension/src/git/GitIntegrationModule.ts" -check_file "packages/vscode-extension/src/diff/DiffGenerator.ts" -check_file "packages/vscode-extension/src/websocket/WebSocketClient.ts" -check_file "packages/relay-server/src/index.ts" -check_file "packages/mobile-client/src/App.tsx" -check_file "packages/mobile-client/src/components/DiffViewer.tsx" -check_file "packages/mobile-client/src/websocket/WebSocketClient.ts" -check_file "packages/protocol/src/index.ts" -echo "" - -# Check VS Code configuration -echo "πŸ”§ Checking VS Code configuration..." -check_file ".vscode/launch.json" -echo "" - -# Check required commands -echo "πŸ’» Checking required commands..." -check_command "node" -check_command "npm" -check_command "git" -echo "" - -# Check Node.js version -echo "πŸ” Checking Node.js version..." -NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) -if [ "$NODE_VERSION" -ge 20 ]; then - echo -e "${GREEN}βœ“${NC} Node.js version is 20.x or higher" -else - echo -e "${RED}βœ—${NC} Node.js version is below 20.x (current: $(node --version))" - ERRORS=$((ERRORS + 1)) -fi -echo "" - -# Check if packages are built -echo "πŸ—οΈ Checking if packages are built..." -if [ -f "packages/protocol/dist/index.js" ]; then - echo -e "${GREEN}βœ“${NC} Protocol package is built" -else - echo -e "${YELLOW}⚠${NC} Protocol package not built (run: npm run build)" - WARNINGS=$((WARNINGS + 1)) -fi - -if [ -f "packages/relay-server/dist/index.js" ]; then - echo -e "${GREEN}βœ“${NC} Relay server package is built" -else - echo -e "${YELLOW}⚠${NC} Relay server package not built (run: npm run build)" - WARNINGS=$((WARNINGS + 1)) -fi - -if [ -f "packages/vscode-extension/dist/extension.js" ]; then - echo -e "${GREEN}βœ“${NC} VS Code extension package is built" -else - echo -e "${YELLOW}⚠${NC} VS Code extension package not built (run: npm run build)" - WARNINGS=$((WARNINGS + 1)) -fi - -if [ -f "packages/mobile-client/dist/index.html" ]; then - echo -e "${GREEN}βœ“${NC} Mobile client package is built" -else - echo -e "${YELLOW}⚠${NC} Mobile client package not built (run: npm run build)" - WARNINGS=$((WARNINGS + 1)) -fi -echo "" - -# Check Git repository -echo "πŸ” Checking Git repository..." -if git rev-parse --git-dir > /dev/null 2>&1; then - echo -e "${GREEN}βœ“${NC} Git repository initialized" - - # Check if there are commits - if git log -1 > /dev/null 2>&1; then - echo -e "${GREEN}βœ“${NC} Git repository has commits" - else - echo -e "${YELLOW}⚠${NC} Git repository has no commits (some tests may not work)" - WARNINGS=$((WARNINGS + 1)) - fi -else - echo -e "${RED}βœ—${NC} Not a Git repository" - ERRORS=$((ERRORS + 1)) -fi -echo "" - -# Summary -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "πŸ“Š Verification Summary" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then - echo -e "${GREEN}βœ… All checks passed!${NC}" - echo "" - echo "πŸš€ Ready to start manual testing!" - echo "" - echo "Next steps:" - echo " 1. Generate test files: ./scripts/generate-test-files.sh" - echo " 2. Start relay server: cd packages/relay-server && npm start" - echo " 3. Start mobile client: cd packages/mobile-client && npm run dev" - echo " 4. Press F5 in VS Code to launch Extension Development Host" - echo " 5. Follow MANUAL_TEST_EXECUTION.md for testing procedures" - exit 0 -elif [ $ERRORS -eq 0 ]; then - echo -e "${YELLOW}⚠ $WARNINGS warning(s) found${NC}" - echo "" - echo "You can proceed with manual testing, but some features may not work optimally." - echo "Review the warnings above and fix them if needed." - exit 0 -else - echo -e "${RED}βœ— $ERRORS error(s) found${NC}" - if [ $WARNINGS -gt 0 ]; then - echo -e "${YELLOW}⚠ $WARNINGS warning(s) found${NC}" - fi - echo "" - echo "Please fix the errors above before proceeding with manual testing." - echo "" - echo "Common fixes:" - echo " - Run: npm run build" - echo " - Run: chmod +x scripts/*.sh" - echo " - Install Node.js 20.x or higher" - echo " - Initialize Git repository: git init" - exit 1 -fi diff --git a/services/auth/Dockerfile b/services/auth/Dockerfile new file mode 100644 index 0000000..089c423 --- /dev/null +++ b/services/auth/Dockerfile @@ -0,0 +1,21 @@ +# Build +FROM golang:1.22-alpine AS builder + +WORKDIR /app + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o auth-service ./cmd/server + +# Run +FROM alpine:3.19 + +RUN apk --no-cache add ca-certificates tzdata + +WORKDIR /app +COPY --from=builder /app/auth-service . + +EXPOSE 8081 +CMD [ "./auth-service" ] \ No newline at end of file diff --git a/services/auth/cmd/server/main.go b/services/auth/cmd/server/main.go new file mode 100644 index 0000000..2473b66 --- /dev/null +++ b/services/auth/cmd/server/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "context" + "database/sql" + "log" + "net/http" + + "github.com/gagan-devv/codelink/services/auth/internal/config" + authcrypto "github.com/gagan-devv/codelink/services/auth/internal/crypto" + "github.com/gagan-devv/codelink/services/auth/internal/db" + "github.com/gagan-devv/codelink/services/auth/internal/handlers" + "github.com/gagan-devv/codelink/services/auth/internal/middleware" + "github.com/gagan-devv/codelink/services/auth/internal/repository" + "github.com/gin-gonic/gin" + "github.com/joho/godotenv" + "github.com/pressly/goose/v3" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func main() { + ctx := context.Background() + + if err := godotenv.Load("../../infra/.env"); err != nil { + log.Println("no .env file found, reading from environment") + } + cfg, err := config.Load() + if err != nil { + log.Fatalf("config: %v", err) + } + + // Databases + pgPool, err := db.NewPostgresPool(ctx, cfg.PostgresURL) + if err != nil { + log.Fatalf("postgres: %v", err) + } + defer pgPool.Close() + + redisClient, err := db.NewRedisClient(ctx, cfg.RedisURL) + if err != nil { + log.Fatalf("redis: %v", err) + } + defer redisClient.Close() + + // Migrations + sqlDB, err := sql.Open("pgx", cfg.PostgresURL) + if err != nil { + log.Fatalf("sql.Opn: %v", err) + } + defer sqlDB.Close() + + goose.SetBaseFS(db.MigrationsFS) + if err := goose.SetDialect("postgres"); err != nil { + log.Fatalf("goose dialect: %v", err) + } + if err := goose.Up(sqlDB, "migrations"); err != nil { + log.Fatalf("migrations: %v", err) + } + log.Println("migrations: up to date") + + // JWT Signer + signer, err := authcrypto.NewJWTSigner(cfg.JWTPrivateKeyPEM, cfg.JWTIssuer) + if err != nil { + log.Fatalf("jwt signer: %v", err) + } + + // Repositories + laptopRepo := repository.NewLaptopRepository(pgPool) + sessionRepo := repository.NewSessionRepository(pgPool, redisClient) + + // Handlers + laptopHandler := handlers.NewLaptopHandler(laptopRepo) + sessionHandler := handlers.NewSessionHandler(sessionRepo, signer, cfg) + + // Router + r := gin.Default() + r.Use(corsMiddleware()) + r.GET("/healthz", func(c *gin.Context) { c.Status(http.StatusOK)}) + + v1 := r.Group("/v1") + + v1.POST("/laptops/register", laptopHandler.Register) + + sessions := v1.Group("/sessions") + { + sessions.POST("/:id/join", sessionHandler.Join) + + authed := sessions.Group("", middleware.LaptopAuth(laptopRepo)) + authed.POST("", sessionHandler.Create) + authed.GET("/:id/status", sessionHandler.Status) + authed.DELETE("/:id", sessionHandler.Revoke) + } + + // Server + addr := ":" + cfg.Port + log.Printf("auth service listening on %s", addr) + if err := http.ListenAndServe(addr, r); err != nil { + log.Fatalf("server: %v", err) + } +} + +func corsMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS") + c.Header("Access-Control-Allow-Headers", "Content-Type, X-Laptop-Id, X-Laptop-Sig") + + if c.Request.Method == http.MethodOptions { + c.AbortWithStatus(http.StatusNoContent) + return + } + c.Next() + } +} \ No newline at end of file diff --git a/services/auth/go.mod b/services/auth/go.mod new file mode 100644 index 0000000..db2a72e --- /dev/null +++ b/services/auth/go.mod @@ -0,0 +1,54 @@ +module github.com/gagan-devv/codelink/services/auth + +go 1.26.3 + +require ( + github.com/gin-gonic/gin v1.12.0 + github.com/golang-jwt/jwt/v5 v5.3.1 + github.com/google/uuid v1.6.0 + github.com/jackc/pgx/v5 v5.9.2 + github.com/joho/godotenv v1.5.1 + github.com/pressly/goose/v3 v3.27.1 + github.com/redis/go-redis/v9 v9.19.0 +) + +require ( + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.21 // indirect + github.com/mfridman/interpolate v0.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/sethvargo/go-retry v0.3.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/arch v0.22.0 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/services/auth/go.sum b/services/auth/go.sum new file mode 100644 index 0000000..ea88b68 --- /dev/null +++ b/services/auth/go.sum @@ -0,0 +1,139 @@ +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= +github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= +github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pressly/goose/v3 v3.27.1 h1:6uEvcprBybDmW4hcz3gYujhARhye+GoWKhEWyzD5sh4= +github.com/pressly/goose/v3 v3.27.1/go.mod h1:maruOxsPnIG2yHHyo8UqKWXYKFcH7Q76csUV7+7KYoM= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= +github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= +github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= +go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= +go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= +golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/libc v1.72.1 h1:db1xwJ6u1kE3KHTFTTbe2GCrczHPKzlURP0aDC4NGD0= +modernc.org/libc v1.72.1/go.mod h1:HRMiC/PhPGLIPM7GzAFCbI+oSgE3dhZ8FWftmRrHVlY= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/sqlite v1.49.1 h1:dYGHTKcX1sJ+EQDnUzvz4TJ5GbuvhNJa8Fg6ElGx73U= +modernc.org/sqlite v1.49.1/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew= diff --git a/services/auth/internal/config/config.go b/services/auth/internal/config/config.go new file mode 100644 index 0000000..cdb29bd --- /dev/null +++ b/services/auth/internal/config/config.go @@ -0,0 +1,57 @@ +package config + +import ( + "fmt" + "os" +) + +type Config struct { + Port string + PostgresURL string + RedisURL string + HMACSecret string + JWTPrivateKeyPEM string + JWTIssuer string + RelayWSS string +} + +func Load() (*Config, error) { + cfg := &Config{ + Port: getEnv("AUTH_PORT", "8081"), + PostgresURL: os.Getenv("POSTGRES_URL"), + RedisURL: os.Getenv("REDIS_URL"), + HMACSecret: os.Getenv("AUTH_HMAC_SECRET"), + JWTPrivateKeyPEM: os.Getenv("AUTH_PRIVATE_KEY_PEM"), + JWTIssuer: getEnv("AUTH_JWT_ISSUER", "auth.codelink.io"), + RelayWSS: getEnv("RELAY_WSS_URL", "ws://localhost:8082/ws"), + } + if err := cfg.validate(); err != nil { + return nil, err + } + return cfg, nil +} + +func (c *Config) validate() error { + required := map[string]string{ + "POSTGRES_URL": c.PostgresURL, + "REDIS_URL": c.RedisURL, + "AUTH_HMAC_SECRET": c.HMACSecret, + "AUTH_PRIVATE_KEY_PEM": c.JWTPrivateKeyPEM, + } + for name, val := range required { + if val == "" { + return fmt.Errorf("config: required env var %s is not set", name) + } + } + if len(c.HMACSecret) < 32 { + return fmt.Errorf("config: AUTH_HMAC_SECRET must be at least 32 bytes") + } + return nil +} + +func getEnv(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} diff --git a/services/auth/internal/crypto/crypto_test.go b/services/auth/internal/crypto/crypto_test.go new file mode 100644 index 0000000..c530e15 --- /dev/null +++ b/services/auth/internal/crypto/crypto_test.go @@ -0,0 +1,206 @@ +package crypto_test + +import ( + gocrypto "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "testing" + "time" + + "github.com/gagan-devv/codelink/services/auth/internal/crypto" +) + +func generateTestKeyPair(t *testing.T) (privatePEM, publicPEM string) { + t.Helper() + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("failed to generate test keypair: %v", err) + } + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + t.Fatalf("failed to marshal private key: %v", err) + } + pubBytes, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) + if err != nil { + t.Fatalf("failed to marshal public key: %v", err) + } + privatePEM = string(pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})) + publicPEM = string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubBytes})) + return +} + +func TestParseRSAPublicKey(t *testing.T) { + _, pub := generateTestKeyPair(t) + + t.Run("valid key parses without error", func(t *testing.T) { + if _, err := crypto.ParseRSAPublicKey(pub); err != nil { + t.Errorf("unexpected error: %v", err) + } + }) + + t.Run("empty returns ErrInvalidPEM", func(t *testing.T) { + if _, err := crypto.ParseRSAPublicKey(""); err != crypto.ErrInvalidPEM { + t.Errorf("expected ErrInvalidPEM, got %v", err) + } + }) + + t.Run("garbage input return ErrInvalidPEM", func(t *testing.T) { + if _, err := crypto.ParseRSAPublicKey("not-a-pem"); err != crypto.ErrInvalidPEM { + t.Errorf("expected ErrInvalidPEM, got %v", err) + } + }) +} + +func TestVerifyRequestSignature(t *testing.T) { + priv, pub := generateTestKeyPair(t) + privKey, _ := crypto.ParseRSAPrivateKey(priv) + + message := []byte(`{"laptopId":"lap_abc","requestedAt":1718000000000}`) + digest := sha256.Sum256(message) + sig, err := rsa.SignPKCS1v15(rand.Reader, privKey, gocrypto.SHA256, digest[:]) + if err != nil { + t.Fatalf("sign: %v", err) + } + + t.Run("valid signature verifies", func(t *testing.T) { + if err := crypto.VerifyRequestSignature(pub, message, sig); err != nil { + t.Errorf("valid signature failed: %v", err) + } + }) + t.Run("tampered message fails", func(t *testing.T) { + tampered := []byte(`{"laptopId":"EVIL","requestedAt":1718000000000}`) + if err := crypto.VerifyRequestSignature(pub, tampered, sig); err == nil { + t.Error("expected failure for tampered message") + } + }) + t.Run("wrong public key fails", func(t *testing.T) { + _, otherPub := generateTestKeyPair(t) + if err := crypto.VerifyRequestSignature(otherPub, message, sig); err == nil { + t.Error("expected failure for wrong public key") + } + }) +} + +func TestChallengeRoundTrip(t *testing.T) { + secret := "test-secret-32-bytes-long-xxxxx" + sessionID := "sess_abc123" + requestAt := int64(1718000000000) + + challenge := crypto.GenerateChallenge(sessionID, requestAt, secret) + + t.Run("correct input verify", func(t *testing.T) { + if !crypto.VerifyChallenge(sessionID, requestAt, secret, challenge) { + t.Error("valid challenge should verify") + } + }) + + t.Run("wrong sessionID fails", func(t *testing.T) { + if crypto.VerifyChallenge("sess_EVIL", requestAt, secret, challenge) { + t.Error("wrong sessionID should not verify") + } + }) + + t.Run("wrong timestamp fails", func(t *testing.T) { + if crypto.VerifyChallenge(sessionID, requestAt+1, secret, challenge) { + t.Error("wrong timestamp should not verify") + } + }) + + t.Run("wrong secret fails", func(t *testing.T) { + if crypto.VerifyChallenge(sessionID, requestAt, "different-secret", challenge) { + t.Error("wrong secret should not verify") + } + }) + + t.Run("deterministic β€” same inputs produce same challenge", func(t *testing.T) { + c2 := crypto.GenerateChallenge(sessionID, requestAt, secret) + if challenge != c2 { + t.Errorf("expected deterministic output: %s != %s", challenge, c2) + } + }) +} + +func TestJWTRoundTrip(t *testing.T) { + priv, pub := generateTestKeyPair(t) + issuer := "auth.codelink.io" + + signer, err := crypto.NewJWTSigner(priv, issuer) + if err != nil { + t.Fatalf("NewJWTSigner: %v", err) + } + validator, err := crypto.NewJWTValidator(pub, issuer) + if err != nil { + t.Fatalf("NewJWTValidator: %v", err) + } + + t.Run("host token round-trips", func(t *testing.T) { + claims := crypto.Claims{ + Role: crypto.RoleHost, + SessionID: "sess_xyz", + LaptopID: "lap_abc", + } + tokenStr, err := signer.Issue(claims, 8*time.Hour) + if err != nil { + t.Fatalf("Issue: %v", err) + } + got, err := validator.Validate(tokenStr) + if err != nil { + t.Fatalf("Validate: %v", err) + } + if got.Role != crypto.RoleHost { + t.Errorf("role: wants %s, got %s", crypto.RoleHost, crypto.RoleClient) + } + if got.SessionID != "sess_xyz" { + t.Errorf("session_id: want sess_xyz, got %s", got.SessionID) + } + if got.LaptopID != "lap_abc" { + t.Errorf("laptop_id: want lap_abc, got %s", got.LaptopID) + } + }) + + t.Run("client token round-trips", func(t *testing.T) { + claims := crypto.Claims{ + Role: crypto.RoleClient, + SessionID: "sess_xyz", + MobileDeviceID: "mob_123", + } + tokenStr, _ := signer.Issue(claims, 8*time.Hour) + got, err := validator.Validate(tokenStr) + if err != nil { + t.Fatalf("Validate: %v", err) + } + if got.MobileDeviceID != "mob_123" { + t.Errorf("mobile_device_id: want mob_123, got %s", got.MobileDeviceID) + } + }) + + t.Run("expired token is rejected", func(t *testing.T) { + claims := crypto.Claims{Role: crypto.RoleHost, SessionID: "sess_exp"} + tokenStr, _ := signer.Issue(claims, -1*time.Second) // already expired + if _, err := validator.Validate(tokenStr); err == nil { + t.Error("expired token should be rejected") + } + }) + + t.Run("token signed with different key is rejected", func(t *testing.T) { + otherPriv, _ := generateTestKeyPair(t) + otherSigner, _ := crypto.NewJWTSigner(otherPriv, issuer) + claims := crypto.Claims{Role: crypto.RoleHost, SessionID: "sess_other"} + tokenStr, _ := otherSigner.Issue(claims, time.Hour) + if _, err := validator.Validate(tokenStr); err == nil { + t.Error("token signed with wrong key should be rejected") + } + }) + + t.Run("wrong issuer is rejected", func(t *testing.T) { + wrongIssuerValidator, _ := crypto.NewJWTValidator(pub, "evil.example.com") + claims := crypto.Claims{Role: crypto.RoleHost, SessionID: "sess_iss"} + tokenStr, _ := signer.Issue(claims, time.Hour) + if _, err := wrongIssuerValidator.Validate(tokenStr); err == nil { + t.Error("wrong issuer should be rejected") + } + }) +} diff --git a/services/auth/internal/crypto/hmac.go b/services/auth/internal/crypto/hmac.go new file mode 100644 index 0000000..8d98314 --- /dev/null +++ b/services/auth/internal/crypto/hmac.go @@ -0,0 +1,19 @@ +package crypto + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "fmt" +) + +func GenerateChallenge(sessionID string, requestedAt int64, secret string) string { + mac := hmac.New(sha256.New, []byte(secret)) + mac.Write([]byte(fmt.Sprintf("%s:%d", sessionID, requestedAt))) + return hex.EncodeToString(mac.Sum(nil)) +} + +func VerifyChallenge(sessionID string, requestedAt int64, secret, provided string) bool { + expected := GenerateChallenge(sessionID, requestedAt, secret) + return hmac.Equal([]byte(expected), []byte(provided)) +} \ No newline at end of file diff --git a/services/auth/internal/crypto/jwt.go b/services/auth/internal/crypto/jwt.go new file mode 100644 index 0000000..5b8f230 --- /dev/null +++ b/services/auth/internal/crypto/jwt.go @@ -0,0 +1,88 @@ +package crypto + +import ( + "crypto/rsa" + "errors" + "fmt" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +type Role string + +const ( + RoleHost Role = "host" + RoleClient Role = "client" +) + +type Claims struct { + jwt.RegisteredClaims + Role Role `json:"role"` + SessionID string `json:"session_id"` + LaptopID string `json:"laptop_id,omitempty"` + MobileDeviceID string `json:"mobile_device_id,omitempty"` + + // Permissions []string `json:"permissions,omitempty"` +} + +type JWTSigner struct { + privateKey *rsa.PrivateKey + issuer string +} + +type JWTValidator struct { + publicKey *rsa.PublicKey + issuer string +} + +func NewJWTSigner(privateKeyPEM, issuer string) (*JWTSigner, error) { + key, err := ParseRSAPrivateKey(privateKeyPEM) + if err != nil { + return nil, fmt.Errorf("jwt signer: %w", err) + } + return &JWTSigner{privateKey: key, issuer: issuer}, nil +} + +func NewJWTValidator(publicKeyPEM, issuer string) (*JWTValidator, error) { + key, err := ParseRSAPublicKey(publicKeyPEM) + if err != nil { + return nil, fmt.Errorf("jwt validator: %w", err) + } + return &JWTValidator{publicKey: key, issuer: issuer}, nil +} + +func (s *JWTSigner) Issue(claims Claims, ttl time.Duration) (string, error) { + now := time.Now() + claims.RegisteredClaims = jwt.RegisteredClaims{ + Issuer: s.issuer, + Subject: claims.SessionID, + IssuedAt: jwt.NewNumericDate(now), + ExpiresAt: jwt.NewNumericDate(now.Add(ttl)), + } + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + return token.SignedString(s.privateKey) +} + +func (v *JWTValidator) Validate(tokenStr string) (*Claims, error) { + token, err := jwt.ParseWithClaims( + tokenStr, + &Claims{}, + func(t *jwt.Token) (interface{}, error) { + if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok { + return nil, errors.New("unexpected signing method") + } + return v.publicKey, nil + }, + jwt.WithIssuer(v.issuer), + jwt.WithExpirationRequired(), + ) + if err != nil { + return nil, err + } + claims, ok := token.Claims.(*Claims) + if !ok || !token.Valid { + return nil, errors.New("invalid token claims") + } + return claims, nil +} \ No newline at end of file diff --git a/services/auth/internal/crypto/rsa.go b/services/auth/internal/crypto/rsa.go new file mode 100644 index 0000000..b84e68d --- /dev/null +++ b/services/auth/internal/crypto/rsa.go @@ -0,0 +1,62 @@ +package crypto + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrInvalidPEM = errors.New("crypto: invalid or empty PEM block") + ErrNotRSAPublicKey = errors.New("crypto: PEM block is not an RSA public key") + ErrNotRSAPrivateKey = errors.New("crypto: PEM block is not an RSA private key") +) + +func ParseRSAPublicKey(pemStr string) (*rsa.PublicKey, error) { + block, _ := pem.Decode([]byte(pemStr)) + if block == nil { + return nil, ErrInvalidPEM + } + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, ErrNotRSAPublicKey + } + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, ErrNotRSAPublicKey + } + return rsaPub, nil +} + +func ParseRSAPrivateKey(pemStr string) (*rsa.PrivateKey, error) { + block, _ := pem.Decode([]byte(pemStr)) + if block == nil { + return nil, ErrInvalidPEM + } + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err == nil { + rsaKey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, ErrNotRSAPrivateKey + } + return rsaKey, nil + } + + rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, ErrNotRSAPrivateKey + } + return rsaKey, nil +} + +func VerifyRequestSignature(publicKeyPEM string, message, signature []byte) error { + pub, err := ParseRSAPublicKey(publicKeyPEM) + if err != nil { + return err + } + digest := sha256.Sum256(message) + return rsa.VerifyPKCS1v15(pub, crypto.SHA256, digest[:], signature) +} \ No newline at end of file diff --git a/services/auth/internal/db/embed.go b/services/auth/internal/db/embed.go new file mode 100644 index 0000000..ce9742d --- /dev/null +++ b/services/auth/internal/db/embed.go @@ -0,0 +1,6 @@ +package db + +import "embed" + +//go:embed migrations/*.sql +var MigrationsFS embed.FS \ No newline at end of file diff --git a/services/auth/internal/db/migrations/001_laptops.sql b/services/auth/internal/db/migrations/001_laptops.sql new file mode 100644 index 0000000..3f2cbc1 --- /dev/null +++ b/services/auth/internal/db/migrations/001_laptops.sql @@ -0,0 +1,10 @@ +-- +goose Up +CREATE TABLE laptops ( + id TEXT PRIMARY KEY, + public_key_pem TEXT NOT NULL UNIQUE, + registered_at TIMESTAMPTZ NOT NULL DEFAULT now(), + last_seen_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- +goose Down +DROP TABLE laptops; \ No newline at end of file diff --git a/services/auth/internal/db/migrations/002_sessions.sql b/services/auth/internal/db/migrations/002_sessions.sql new file mode 100644 index 0000000..4a5337b --- /dev/null +++ b/services/auth/internal/db/migrations/002_sessions.sql @@ -0,0 +1,21 @@ +-- +goose Up +CREATE TYPE session_state as ENUM ('pending', 'active', 'revoked', 'expired'); + +CREATE TABLE sessions ( + id TEXT PRIMARY KEY, + laptop_id TEXT NOT NULL REFERENCES laptops(id), + mobile_device_id TEXT, + state session_state NOT NULL DEFAULT 'pending', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + approved_at TIMESTAMPTZ, + revoked_at TIMESTAMPTZ, + expires_at TIMESTAMPTZ NOT NULL +); + +CREATE INDEX idx_sessions_laptop_id ON sessions(laptop_id); +CREATE INDEX idx_session_state ON sessions(state); + + +-- +goose Down +DROP TABLE sessions; +DROP TYPE session_state; \ No newline at end of file diff --git a/services/auth/internal/db/postgres.go b/services/auth/internal/db/postgres.go new file mode 100644 index 0000000..c8f6f14 --- /dev/null +++ b/services/auth/internal/db/postgres.go @@ -0,0 +1,19 @@ +package db + +import ( + "context" + "fmt" + + "github.com/jackc/pgx/v5/pgxpool" +) + +func NewPostgresPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) { + pool, err := pgxpool.New(ctx, dsn) + if err != nil { + return nil, fmt.Errorf("postgres: failed to create pool: %w", err) + } + if err := pool.Ping(ctx); err != nil { + return nil, fmt.Errorf("postgres: ping failed: %w", err) + } + return pool, nil +} \ No newline at end of file diff --git a/services/auth/internal/db/redis.go b/services/auth/internal/db/redis.go new file mode 100644 index 0000000..6a795d5 --- /dev/null +++ b/services/auth/internal/db/redis.go @@ -0,0 +1,20 @@ +package db + +import ( + "context" + "fmt" + + "github.com/redis/go-redis/v9" +) + +func NewRedisClient(ctx context.Context, rawURL string) (*redis.Client, error) { + opts, err := redis.ParseURL(rawURL) + if err != nil { + return nil, fmt.Errorf("redis: invalid URL: %w", err) + } + client := redis.NewClient(opts) + if err := client.Ping(ctx).Err(); err != nil { + return nil, fmt.Errorf("redis: ping failed: %w", err) + } + return client, nil +} \ No newline at end of file diff --git a/services/auth/internal/domain/laptop.go b/services/auth/internal/domain/laptop.go new file mode 100644 index 0000000..946ea62 --- /dev/null +++ b/services/auth/internal/domain/laptop.go @@ -0,0 +1,10 @@ +package domain + +import "time" + +type Laptop struct { + ID string + PublicKeyPEM string + RegisteredAt time.Time + LastSeenAt time.Time +} diff --git a/services/auth/internal/domain/session.go b/services/auth/internal/domain/session.go new file mode 100644 index 0000000..72244fe --- /dev/null +++ b/services/auth/internal/domain/session.go @@ -0,0 +1,23 @@ +package domain + +import "time" + +type SessionState string + +const ( + SessionPending SessionState = "pending" + SessionActive SessionState = "active" + SessionRevoked SessionState = "revoked" + SessionExpired SessionState = "expired" +) + +type Session struct { + ID string + LaptopID string + MobileDeviceID string + State SessionState + CreatedAt time.Time + ApprovedAt time.Time + RevokedAt time.Time + ExpiresAt time.Time +} diff --git a/services/auth/internal/handlers/laptops.go b/services/auth/internal/handlers/laptops.go new file mode 100644 index 0000000..de4072d --- /dev/null +++ b/services/auth/internal/handlers/laptops.go @@ -0,0 +1,56 @@ +package handlers + +import ( + "net/http" + "time" + + "github.com/gagan-devv/codelink/services/auth/internal/crypto" + "github.com/gagan-devv/codelink/services/auth/internal/domain" + "github.com/gagan-devv/codelink/services/auth/internal/repository" + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type LaptopHandler struct { + laptopRepo *repository.LaptopRepository +} + +func NewLaptopHandler(repo *repository.LaptopRepository) *LaptopHandler { + return &LaptopHandler{laptopRepo: repo} +} + +type registerRequest struct { + PublicKeyPEM string `json:"publicKeyPem" binding:"required"` +} + +func (h *LaptopHandler) Register(c *gin.Context) { + var req registerRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if _, err := crypto.ParseRSAPublicKey(req.PublicKeyPEM); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid RSA public key PEM"}) + return + } + + existing, err := h.laptopRepo.GetByPublicKey(c.Request.Context(), req.PublicKeyPEM) + if err == nil { + c.JSON(http.StatusOK, gin.H{"laptopId": existing.ID}) + return + } + + laptop := &domain.Laptop{ + ID: "lap_" + uuid.New().String(), + PublicKeyPEM: req.PublicKeyPEM, + RegisteredAt: time.Now().UTC(), + LastSeenAt: time.Now().UTC(), + } + if err := h.laptopRepo.Create(c.Request.Context(), laptop); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to register"}) + return + } + + c.JSON(http.StatusCreated, gin.H{"laptopId": laptop.ID}) +} diff --git a/services/auth/internal/handlers/sessions.go b/services/auth/internal/handlers/sessions.go new file mode 100644 index 0000000..68d4c41 --- /dev/null +++ b/services/auth/internal/handlers/sessions.go @@ -0,0 +1,206 @@ +package handlers + +import ( + "crypto/subtle" + "encoding/base64" + "encoding/json" + "net/http" + "time" + + "github.com/gagan-devv/codelink/services/auth/internal/config" + authcrypto "github.com/gagan-devv/codelink/services/auth/internal/crypto" + "github.com/gagan-devv/codelink/services/auth/internal/domain" + "github.com/gagan-devv/codelink/services/auth/internal/repository" + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type SessionHandler struct { + sessionRepo *repository.SessionRepository + signer *authcrypto.JWTSigner + cfg *config.Config +} + +func NewSessionHandler( + sessionRepo *repository.SessionRepository, + signer *authcrypto.JWTSigner, + cfg *config.Config, +) *SessionHandler { + return &SessionHandler{sessionRepo: sessionRepo, signer: signer, cfg: cfg} +} + +type qrData struct { + SessionID string `json:"sessionId"` + Challenge string `json:"challenge"` + RelayWSS string `json:"relayWSS"` +} + +func (h *SessionHandler) Create(c *gin.Context) { + laptopID := c.GetString("laptopID") + + var req struct { + LaptopID string `json:"laptopId" binding:"required"` + RequestedAt int64 `json:"requestedAt" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if req.LaptopID != laptopID { + c.JSON(http.StatusForbidden, gin.H{"error": "laptopId does not match authenticated laptop"}) + return + } + + sessionID := "sess_" + uuid.New().String() + challenge := authcrypto.GenerateChallenge(sessionID, req.RequestedAt, h.cfg.HMACSecret) + now := time.Now().UTC() + expiresAt := now.Add(90 * time.Second) + + session := &domain.Session{ + ID: sessionID, + LaptopID: laptopID, + State: domain.SessionPending, + CreatedAt: now, + ExpiresAt: expiresAt, + } + if err := h.sessionRepo.Create(c.Request.Context(), session, challenge); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create session"}) + return + } + + qr, _ := json.Marshal(qrData{ + SessionID: sessionID, + Challenge: challenge, + RelayWSS: h.cfg.RelayWSS, + }) + + c.JSON(http.StatusCreated, gin.H{ + "sessionId": sessionID, + "qrPayload": base64.URLEncoding.EncodeToString(qr), + "expiresAt": expiresAt.UnixMilli(), + }) +} + +func (h *SessionHandler) Status(c *gin.Context) { + sessionID := c.Param("id") + laptopID := c.GetString("laptopID") + + data, err := h.sessionRepo.GetRedisSession(c.Request.Context(), sessionID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "session not found or expired"}) + return + } + if data.LaptopID != laptopID { + c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"}) + return + } + + if data.State == string(domain.SessionActive) { + c.JSON(http.StatusOK, gin.H{ + "state": "active", + "mobileDeviceId": data.MobileDeviceID, + "laptopToken": data.LaptopToken, + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "state": data.State, + "mobileDeviceId": nil, + "laptopToken": nil, + }) +} + +func (h *SessionHandler) Join(c *gin.Context) { + sessionID := c.Param("id") + + var req struct { + Challenge string `json:"challenge" binding:"required"` + MobileDeviceID string `json:"mobileDeviceId" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + data, err := h.sessionRepo.GetRedisSession(c.Request.Context(), sessionID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "session not found or expired"}) + return + } + + switch domain.SessionState(data.State) { + case domain.SessionRevoked: + c.JSON(http.StatusGone, gin.H{"error": "session has been revoked"}) + return + case domain.SessionActive: + c.JSON(http.StatusConflict, gin.H{"error": "session already active"}) + return + case domain.SessionPending: + default: + c.JSON(http.StatusBadRequest, gin.H{"error": "session not joinable"}) + } + + if subtle.ConstantTimeCompare([]byte(req.Challenge), []byte(data.Challenge)) != 1 { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid challenge"}) + return + } + + ttl := 8 * time.Hour + laptopToken, err := h.signer.Issue(authcrypto.Claims{ + Role: authcrypto.RoleHost, + SessionID: sessionID, + LaptopID: data.LaptopID, + }, ttl) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to issue laptop token"}) + return + } + + mobileToken, err := h.signer.Issue(authcrypto.Claims{ + Role: authcrypto.RoleClient, + SessionID: sessionID, + MobileDeviceID: req.MobileDeviceID, + }, ttl) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to issue mobile token"}) + return + } + + if err := h.sessionRepo.Activate( + c.Request.Context(), + sessionID, req.MobileDeviceID, data.LaptopID, laptopToken, mobileToken, + ); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to activate session"}) + return + } + + c.JSON(http.StatusOK, gin.H{ + "mobileToken": mobileToken, + "relayWss": h.cfg.RelayWSS, + "sessionId": sessionID, + "expiresAt": time.Now().Add(ttl).UnixMilli(), + }) +} + +func (h *SessionHandler) Revoke(c *gin.Context) { + sessionID := c.Param("id") + laptopID := c.GetString("laptopID") + + data, err := h.sessionRepo.GetRedisSession(c.Request.Context(), sessionID) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "session not found"}) + return + } + if data.LaptopID != laptopID { + c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"}) + return + } + + if err := h.sessionRepo.Revoke(c.Request.Context(), sessionID); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to revoke session"}) + return + } + + c.Status(http.StatusNoContent) +} \ No newline at end of file diff --git a/services/auth/internal/middleware/laptop_auth.go b/services/auth/internal/middleware/laptop_auth.go new file mode 100644 index 0000000..857a02d --- /dev/null +++ b/services/auth/internal/middleware/laptop_auth.go @@ -0,0 +1,54 @@ +package middleware + +import ( + "bytes" + "encoding/base64" + "io" + "net/http" + + "github.com/gagan-devv/codelink/services/auth/internal/crypto" + "github.com/gagan-devv/codelink/services/auth/internal/repository" + "github.com/gin-gonic/gin" +) + +func LaptopAuth(laptopRepo *repository.LaptopRepository) gin.HandlerFunc { + return func(c *gin.Context) { + laptopID := c.GetHeader("X-Laptop-Id") + sigB64 := c.GetHeader("X-Laptop-Sig") + + if laptopID == "" || sigB64 == "" { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ + "error": "missing X-Laptop-Id or X-Laptop-Sig header", + }) + return + } + + body, err := io.ReadAll(c.Request.Body) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "failed to read body"}) + return + } + c.Request.Body = io.NopCloser(bytes.NewReader(body)) + + laptop, err := laptopRepo.GetByID(c.Request.Context(), laptopID) + if err != nil { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unknown lapotp"}) + return + } + + sig, err := base64.StdEncoding.DecodeString(sigB64) + if err != nil { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "malformed signature"}) + return + } + + if err := crypto.VerifyRequestSignature(laptop.PublicKeyPEM, body, sig); err != nil { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "signature verification failed"}) + return + } + + c.Set("laptopID", laptop.ID) + c.Set("laptop", laptop) + c.Next() + } +} diff --git a/services/auth/internal/repository/laptop_repo.go b/services/auth/internal/repository/laptop_repo.go new file mode 100644 index 0000000..023071a --- /dev/null +++ b/services/auth/internal/repository/laptop_repo.go @@ -0,0 +1,76 @@ +package repository + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/gagan-devv/codelink/services/auth/internal/domain" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" +) + +var ErrLaptopNotFound = errors.New("laptop not found") + +type LaptopRepository struct { + db *pgxpool.Pool +} + +func NewLaptopRepository(db *pgxpool.Pool) *LaptopRepository { + return &LaptopRepository{db: db} +} + +func (r *LaptopRepository) Create(ctx context.Context, laptop *domain.Laptop) error { + _, err := r.db.Exec(ctx, ` + INSERT INTO laptops (id, public_key_pem, registered_at, last_seen_at) + VALUES ($1, $2, $3, $4) + ON CONFLICT (id) DO NOTHING + `, laptop.ID, laptop.PublicKeyPEM, laptop.RegisteredAt, laptop.LastSeenAt) + if err != nil { + return fmt.Errorf("laptop repo: create: %w", err) + } + return nil +} + +func (r *LaptopRepository) GetByID(ctx context.Context, id string) (*domain.Laptop, error) { + row := r.db.QueryRow(ctx, ` + SELECT id, public_key_pem, registered_at, last_seen_at + FROM laptops WHERE id = $1 + `, id) + + l := &domain.Laptop{} + err := row.Scan(&l.ID, &l.PublicKeyPEM, &l.RegisteredAt, &l.LastSeenAt) + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrLaptopNotFound + } + if err != nil { + return nil, fmt.Errorf("laptop repo: get by id: %w", err) + } + return l, nil +} + +func (r *LaptopRepository) GetByPublicKey(ctx context.Context, pubKeyPEM string) (*domain.Laptop, error) { + row := r.db.QueryRow(ctx, ` + SELECT id, public_key_pem, registered_at, last_seen_at + FROM laptops WHERE public_key_pem = $1 + `, pubKeyPEM) + + l := &domain.Laptop{} + err := row.Scan(&l.ID, &l.PublicKeyPEM, &l.RegisteredAt, &l.LastSeenAt) + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrLaptopNotFound + } + if err != nil { + return nil, fmt.Errorf("laptop repo: get by public key: %w", err) + } + return l, nil +} + +func (r *LaptopRepository) UpdateLastSeen(ctx context.Context, id string) error { + _, err := r.db.Exec(ctx, + `UPDATE laptops SET last_seen_at = $1 WHERE id = $2`, + time.Now(), id, + ) + return err +} \ No newline at end of file diff --git a/services/auth/internal/repository/laptop_repo_test.go b/services/auth/internal/repository/laptop_repo_test.go new file mode 100644 index 0000000..4d3f8ad --- /dev/null +++ b/services/auth/internal/repository/laptop_repo_test.go @@ -0,0 +1,60 @@ +package repository_test + +import ( + "context" + "testing" + "time" + + "github.com/google/uuid" + + "github.com/gagan-devv/codelink/services/auth/internal/domain" + "github.com/gagan-devv/codelink/services/auth/internal/repository" +) + +func TestLaptopRepository(t *testing.T) { + pool := testDB(t) + repo := repository.NewLaptopRepository(pool) + ctx := context.Background() + + laptop := &domain.Laptop{ + ID: "lap_" + uuid.New().String(), + PublicKeyPEM: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkq" + uuid.New().String() + "\n-----END PUBLIC KEY-----", + RegisteredAt: time.Now().UTC(), + LastSeenAt: time.Now().UTC(), + } + + t.Run("Create succeeds", func(t *testing.T) { + if err := repo.Create(ctx, laptop); err != nil { + t.Fatalf("Create: %v", err) + } + }) + + t.Run("Create is idempotent", func(t *testing.T) { + if err := repo.Create(ctx, laptop); err != nil { + t.Fatalf("second Create should not fail: %v", err) + } + }) + + t.Run("GetByID returns the laptop", func(t *testing.T) { + got, err := repo.GetByID(ctx, laptop.ID) + if err != nil { + t.Fatalf("GetByID: %v", err) + } + if got.ID != laptop.ID { + t.Errorf("id: want %s, got %s", laptop.ID, got.ID) + } + }) + + t.Run("GetByID unknown returns ErrLaptopNotFound", func(t *testing.T) { + _, err := repo.GetByID(ctx, "lap_doesnotexist") + if err != repository.ErrLaptopNotFound { + t.Errorf("want ErrLaptopNotFound, got %v", err) + } + }) + + t.Run("UpdateLastSeen does not error", func(t *testing.T) { + if err := repo.UpdateLastSeen(ctx, laptop.ID); err != nil { + t.Errorf("UpdateLastSeen: %v", err) + } + }) +} \ No newline at end of file diff --git a/services/auth/internal/repository/session_repo.go b/services/auth/internal/repository/session_repo.go new file mode 100644 index 0000000..b745860 --- /dev/null +++ b/services/auth/internal/repository/session_repo.go @@ -0,0 +1,152 @@ +package repository + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/gagan-devv/codelink/services/auth/internal/domain" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/redis/go-redis/v9" +) + +var ErrSessionNotFound = errors.New("sessoin not found") + +const ( + pendingTTL = 90 * time.Second + activeTTL = 8 * time.Hour + revokedTTL = 2 * time.Minute +) + +type redisSession struct { + State string `json:"state"` + LaptopID string `json:"laptop_id"` + MobileDeviceID string `json:"mobile_device_id,omitempty"` + Challenge string `json:"challenge,omitempty"` + LaptopToken string `json:"laptop_token,omitempty"` + MobileToken string `json:"mobile_token,omitempty"` +} + +type SessionRepository struct { + db *pgxpool.Pool + redis *redis.Client +} + +func NewSessionRepository(db *pgxpool.Pool, rdb *redis.Client) *SessionRepository { + return &SessionRepository{db: db, redis: rdb} +} + +func (r *SessionRepository) Create(ctx context.Context, s *domain.Session, challenge string) error { + _, err := r.db.Exec(ctx, ` + INSERT INTO sessions (id, laptop_id, state, created_at, expires_at) + VALUES ($1, $2, $3, $4, $5) + `, s.ID, s.LaptopID, string(s.State), s.CreatedAt, s.ExpiresAt) + if err != nil { + return fmt.Errorf("session repo: create postgres: %w", err) + } + + data := redisSession{ + State: string(domain.SessionPending), + LaptopID: s.LaptopID, + Challenge: challenge, + } + if err := r.setRedis(ctx, s.ID, data, pendingTTL); err != nil { + return fmt.Errorf("session repo: create redis: %w", err) + } + return nil +} + +func (r *SessionRepository) GetRedisSession(ctx context.Context, sessionID string) (*redisSession, error) { + b, err := r.redis.Get(ctx, redisKey(sessionID)).Bytes() + if errors.Is(err, redis.Nil) { + return nil, ErrSessionNotFound + } + if err != nil { + return nil, fmt.Errorf("session repo: redis get: %w", err) + } + var data redisSession + if err := json.Unmarshal(b, &data); err != nil { + return nil, fmt.Errorf("session repo: unmarshal: %w", err) + } + return &data, nil +} + +func (r *SessionRepository) Activate( + ctx context.Context, + sessionID, mobileDeviceID, laptopID, laptopToken, mobileToken string, +) error { + now := time.Now() + _, err := r.db.Exec(ctx, ` + UPDATE sessions + SET state = 'active', mobile_device_id = $1, approved_at = $2 + WHERE id = $3 + `, mobileDeviceID, now, sessionID) + if err != nil { + return fmt.Errorf("session repo: activate postgres: %w", err) + } + + data := redisSession{ + State: string(domain.SessionActive), + LaptopID: laptopID, + MobileDeviceID: mobileDeviceID, + LaptopToken: laptopToken, + MobileToken: mobileToken, + } + if err := r.setRedis(ctx, sessionID, data, activeTTL); err != nil { + return fmt.Errorf("session repo: activate redis: %w", err) + } + return nil +} + +func (r *SessionRepository) Revoke(ctx context.Context, sessionID string) error { + _, err := r.db.Exec(ctx, ` + UPDATE sessions SET state = 'revoked', revoked_at = $1 WHERE id = $2 + `, time.Now(), sessionID) + if err != nil { + return fmt.Errorf("session repo: revoke postgres: %w", err) + } + + data := redisSession{State: string(domain.SessionRevoked)} + if err := r.setRedis(ctx, sessionID, data, revokedTTL); err != nil { + return fmt.Errorf("session repo: revoke redis: %w", err) + } + return nil +} + +func (r *SessionRepository) GetByID(ctx context.Context, sessionID string) (*domain.Session, error) { + row := r.db.QueryRow(ctx, ` + SELECT id, laptop_id, mobile_device_id, state, + created_at, approved_at, revoked_at, expires_at + FROM sessions WHERE id = $1 + `, sessionID) + + s := &domain.Session{} + var state string + err := row.Scan( + &s.ID, &s.LaptopID, &s.MobileDeviceID, &state, + &s.CreatedAt, &s.ApprovedAt, &s.RevokedAt, &s.ExpiresAt, + ) + if errors.Is(err, pgx.ErrNoRows) { + return nil, ErrSessionNotFound + } + if err != nil { + return nil, fmt.Errorf("session repo: get by id: %w", err) + } + s.State = domain.SessionState(state) + return s, nil +} + +func (r *SessionRepository) setRedis(ctx context.Context, sessionID string, data redisSession, ttl time.Duration) error { + b, err := json.Marshal(data) + if err != nil { + return err + } + return r.redis.Set(ctx, redisKey(sessionID), b, ttl).Err() +} + +func redisKey(sessionID string) string { + return "session:" + sessionID +} \ No newline at end of file diff --git a/services/auth/internal/repository/testhelper_test.go b/services/auth/internal/repository/testhelper_test.go new file mode 100644 index 0000000..58800ac --- /dev/null +++ b/services/auth/internal/repository/testhelper_test.go @@ -0,0 +1,39 @@ +package repository_test + +import ( + "context" + "os" + "testing" + + "github.com/gagan-devv/codelink/services/auth/internal/db" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/redis/go-redis/v9" +) + +func testDB(t *testing.T) *pgxpool.Pool { + t.Helper() + dsn := os.Getenv("TEST_POSTGRES_URL") + if dsn == "" { + dsn = "postgres://codelink:devpassword@localhost:5432/codelink" + } + pool, err := db.NewPostgresPool(context.Background(), dsn) + if err != nil { + t.Skipf("postgres unavailable (run docker compose up): %v", err) + } + t.Cleanup(func() { pool.Close() }) + return pool +} + +func testRedis(t *testing.T) *redis.Client { + t.Helper() + url := os.Getenv("TEST_REDIS_URL") + if url == "" { + url = "redis://localhost:6379" + } + client, err := db.NewRedisClient(context.Background(), url) + if err != nil { + t.Skipf("redis unavailable (run docker compose up): %v", err) + } + t.Cleanup(func() { client.Close() }) + return client +} \ No newline at end of file diff --git a/services/relay/Dockerfile b/services/relay/Dockerfile new file mode 100644 index 0000000..fb0f27b --- /dev/null +++ b/services/relay/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:1.22-alpine AS builder +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o relay-service ./cmd/server + +FROM alpine:3.19 +RUN apk --no-cache add ca-certificates tzdata +WORKDIR /app +COPY --from=builder /app/relay-service . +EXPOSE 8082 +CMD [ "./relay-service" ] \ No newline at end of file diff --git a/services/relay/cmd/server/main.go b/services/relay/cmd/server/main.go new file mode 100644 index 0000000..77ab310 --- /dev/null +++ b/services/relay/cmd/server/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/gagan-devv/codelink/services/relay/internal/auth" + "github.com/gagan-devv/codelink/services/relay/internal/config" + "github.com/gagan-devv/codelink/services/relay/internal/relay" + "github.com/gagan-devv/codelink/services/relay/internal/session" + "github.com/joho/godotenv" + "github.com/redis/go-redis/v9" +) + +func main() { + _ = godotenv.Load("../../infra/.env") + + cfg, err := config.Load() + if err != nil { + log.Fatalf("config: %v", err) + } + + validator, err := auth.NewValidator(cfg.AuthPublicKeyPEM, cfg.JWTIssuer) + if err != nil { + log.Fatalf("jwt validator: %v", err) + } + + opts, err := redis.ParseURL(cfg.RedisURL) + if err != nil { + log.Fatalf("redis URL: %v", err) + } + rdb := redis.NewClient(opts) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if err := rdb.Ping(ctx).Err(); err != nil { + log.Fatalf("redis ping: %v", err) + } + log.Println("relay: redis connected") + + manager := session.NewManager() + go manager.WatchRevocations(ctx, rdb) + + wsHandler := relay.NewHandler(manager, validator) + + mux := http.NewServeMux() + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + mux.Handle("/ws", wsHandler) + + srv := &http.Server{ + Addr: ":" + cfg.Port, + Handler: mux, + } + + go func() { + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + <-sig + log.Println("relay: shutting down...") + cancel() + srv.Shutdown(context.Background()) + } () + + log.Printf("relay service listening on :%s", cfg.Port) + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + log.Fatalf("server: %v", err) + } +} \ No newline at end of file diff --git a/services/relay/go.mod b/services/relay/go.mod new file mode 100644 index 0000000..473e261 --- /dev/null +++ b/services/relay/go.mod @@ -0,0 +1,16 @@ +module github.com/gagan-devv/codelink/services/relay + +go 1.26.3 + +require ( + github.com/golang-jwt/jwt/v5 v5.3.1 + github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 + github.com/joho/godotenv v1.5.1 + github.com/redis/go-redis/v9 v9.19.0 +) + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + go.uber.org/atomic v1.11.0 // indirect +) diff --git a/services/relay/go.sum b/services/relay/go.sum new file mode 100644 index 0000000..99d4127 --- /dev/null +++ b/services/relay/go.sum @@ -0,0 +1,30 @@ +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= +github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/services/relay/internal/auth/jwt.go b/services/relay/internal/auth/jwt.go new file mode 100644 index 0000000..77a32d2 --- /dev/null +++ b/services/relay/internal/auth/jwt.go @@ -0,0 +1,68 @@ +package auth + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + + "github.com/golang-jwt/jwt/v5" +) + +type Role string + +const ( + RoleHost Role = "host" + RoleClient Role = "client" +) + +type Claims struct { + jwt.RegisteredClaims + Role Role `json:"role"` + SessionID string `json:"session_id"` +} + +type Validator struct { + publicKey *rsa.PublicKey + issuer string +} + +func NewValidator(publicKeyPEM, issuer string) (*Validator, error) { + block, _ := pem.Decode([]byte(publicKeyPEM)) + if block == nil { + return nil, errors.New("auth: invalid public key PEM") + } + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("auth: parse public key: %w", err) + } + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, errors.New("auth: nt an RSA public key") + } + return &Validator{publicKey: rsaPub, issuer: issuer}, nil +} + +func (v *Validator) Validate(tokenStr string) (*Claims, error) { + token, err := jwt.ParseWithClaims( + tokenStr, + &Claims{}, + func(t *jwt.Token) (interface{}, error) { + if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok { + return nil, errors.New("unexpected signing method") + } + return v.publicKey, nil + }, + jwt.WithIssuer(v.issuer), + jwt.WithExpirationRequired(), + ) + if err != nil { + return nil, err + } + claims, ok := token.Claims.(*Claims) + if !ok || !token.Valid { + return nil, errors.New("invalid token") + } + return claims, nil +} diff --git a/services/relay/internal/config/config.go b/services/relay/internal/config/config.go new file mode 100644 index 0000000..2789957 --- /dev/null +++ b/services/relay/internal/config/config.go @@ -0,0 +1,46 @@ +package config + +import ( + "fmt" + "os" +) + +type Config struct { + Port string + RedisURL string + AuthPublicKeyPEM string + JWTIssuer string +} + +func Load() (*Config, error) { + cfg := &Config{ + Port: getEnv("RELAY_PORT", "8082"), + RedisURL: os.Getenv("REDIS_URL"), + AuthPublicKeyPEM: os.Getenv("AUTH_PUBLIC_KEY_PEM"), + JWTIssuer: getEnv("AUTH_JWT_ISSUER", "auth.codelink.io"), + } + if err := cfg.validate(); err != nil { + return nil, err + } + return cfg, nil +} + +func (c *Config) validate() error { + required := map[string]string{ + "REDIS_URL": c.RedisURL, + "AUTH_PUBLIC_KEY_PEM": c.AuthPublicKeyPEM, + } + for name, val := range required { + if val == "" { + return fmt.Errorf("config: required env var %s is not set", name) + } + } + return nil +} + +func getEnv(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} diff --git a/services/relay/internal/relay/handler.go b/services/relay/internal/relay/handler.go new file mode 100644 index 0000000..e716d1d --- /dev/null +++ b/services/relay/internal/relay/handler.go @@ -0,0 +1,147 @@ +package relay + +import ( + "encoding/json" + "log" + "net/http" + "time" + + "github.com/gagan-devv/codelink/services/relay/internal/auth" + "github.com/gagan-devv/codelink/services/relay/internal/session" + "github.com/google/uuid" + "github.com/gorilla/websocket" +) + +const ( + writeWait = 10 * time.Second + pongWait = 75 * time.Second + pingPeriod = 30 * time.Second + maxMsgSize = 512 * 1024 +) + +var upgrader = websocket.Upgrader{ + HandshakeTimeout: 5 * time.Second, + ReadBufferSize: 32 * 1024, + WriteBufferSize: 32 * 1024, + EnableCompression: true, + CheckOrigin: func(r *http.Request) bool { return true }, +} + +type Handler struct { + manager *session.Manager + validator *auth.Validator +} + +func NewHandler(manager *session.Manager, validator *auth.Validator) *Handler { + return &Handler{manager: manager, validator: validator} +} + +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + tokenStr := r.URL.Query().Get("token") + if tokenStr == "" { + http.Error(w, "missing token", http.StatusUnauthorized) + return + } + claims, err := h.validator.Validate(tokenStr) + if err != nil { + http.Error(w, "invalid token", http.StatusUnauthorized) + return + } + + wsConn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Printf("relay: upgrade error: %v", err) + return + } + + conn := &session.Connection{ + ID: uuid.New().String(), + Conn: wsConn, + SessionID: claims.SessionID, + Role: session.Role(claims.Role), + SendCh: make(chan []byte, 256), + } + + if err := h.manager.Register(conn); err != nil { + log.Printf("relay: register [%s]: %v", conn.ID, err) + return + } + + conn.SendCh <- buildHandshakeAck(conn.ID) + + go h.writePump(conn) + h.readPump(conn) + + h.manager.Unregister(conn) + close(conn.SendCh) +} + +func (h *Handler) readPump(conn *session.Connection) { + conn.Conn.SetReadLimit(maxMsgSize) + conn.Conn.SetReadDeadline(time.Now().Add(pongWait)) + conn.Conn.SetPongHandler(func (string) error { + conn.Conn.SetReadDeadline(time.Now().Add(pongWait)) + return nil + }) + + for { + _, msg, err := conn.Conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, + websocket.CloseGoingAway, + websocket.CloseNormalClosure, + ) { + log.Printf("relay: read [%s]: %v", conn.ID, err) + } + return + } + h.manager.Route(conn, msg) + } +} + +func (h *Handler) writePump(conn *session.Connection) { + ticker := time.NewTicker(pingPeriod) + defer func () { + ticker.Stop() + conn.Conn.Close() + } () + + for { + select { + case msg, ok := <- conn.SendCh: + conn.Conn.SetWriteDeadline(time.Now().Add(writeWait)) + if !ok { + conn.Conn.WriteMessage(websocket.CloseMessage, []byte{}) + return + } + if err := conn.Conn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Printf("relay: write [%s]: %v", conn.ID, err) + return + } + case <- ticker.C: + conn.Conn.SetWriteDeadline(time.Now().Add(writeWait)) + if err := conn.Conn.WriteControl( + websocket.PingMessage, nil, time.Now().Add(writeWait), + ); err != nil { + return + } + } + } +} + +func buildHandshakeAck(connID string) []byte { + type payload struct { + ConnectionID string `json:"connectionID"` + } + type envelope struct { + V int `json:"v"` + Type string `json:"type"` + Payload payload `json:"payload"` + } + b, _ := json.Marshal(envelope{ + V: 1, + Type: "HANDSHAKE_ACK", + Payload: payload{ConnectionID: connID}, + }) + return b +} diff --git a/services/relay/internal/session/connection.go b/services/relay/internal/session/connection.go new file mode 100644 index 0000000..f2ab664 --- /dev/null +++ b/services/relay/internal/session/connection.go @@ -0,0 +1,20 @@ +package session + +import ( + "github.com/gorilla/websocket" +) + +type Role string + +const ( + RoleHost Role = "host" + RoleClient Role = "client" +) + +type Connection struct { + ID string + Conn *websocket.Conn + SessionID string + Role Role + SendCh chan []byte +} diff --git a/services/relay/internal/session/manager.go b/services/relay/internal/session/manager.go new file mode 100644 index 0000000..1e4ec45 --- /dev/null +++ b/services/relay/internal/session/manager.go @@ -0,0 +1,184 @@ +package session + +import ( + "context" + "encoding/json" + "errors" + "log" + "sync" + "time" + + "github.com/redis/go-redis/v9" +) + +var ErrHostAlreadyConnected = errors.New("session already has a host connection") + +type internalSession struct { + mu sync.RWMutex + ID string + Host *Connection + Clients map[string]*Connection +} + +type Manager struct { + mu sync.RWMutex + sessions map[string]*internalSession +} + +func NewManager() *Manager { + return &Manager{sessions: make(map[string]*internalSession)} +} + +func (m *Manager) Register(conn *Connection) error { + m.mu.Lock() + defer m.mu.Unlock() + + sess, ok := m.sessions[conn.SessionID] + if !ok { + sess = &internalSession{ + ID: conn.SessionID, + Clients: make(map[string]*Connection), + } + m.sessions[conn.SessionID] = sess + } + + sess.mu.Lock() + defer sess.mu.Unlock() + + switch conn.Role { + case RoleHost: + if sess.Host != nil { + return ErrHostAlreadyConnected + } + sess.Host = conn + case RoleClient: + sess.Clients[conn.ID] = conn + } + return nil +} + +func (m *Manager) Unregister(conn *Connection) { + m.mu.Lock() + defer m.mu.Unlock() + + sess, ok := m.sessions[conn.ID] + if !ok { + return + } + + sess.mu.Lock() + defer sess.mu.Unlock() + + switch conn.Role { + case RoleHost: + if sess.Host != nil && sess.Host.ID == conn.ID { + sess.Host = nil + } + case RoleClient: + delete(sess.Clients, conn.ID) + } + + if sess.Host == nil && len(sess.Clients) == 0 { + delete(m.sessions, conn.SessionID) + } +} + +func (m *Manager) Route(from *Connection, msg []byte) { + m.mu.RLock() + sess, ok := m.sessions[from.SessionID] + m.mu.RUnlock() + if !ok { + return + } + + sess.mu.RLock() + defer sess.mu.RUnlock() + + switch from.Role { + case RoleHost: + for _, client := range sess.Clients { + safeSend(client.SendCh, msg) + } + case RoleClient: + if sess.Host != nil { + safeSend(sess.Host.SendCh, msg) + } + } +} + +func (m *Manager) WatchRevocations(ctx context.Context, rdb *redis.Client) { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + m.checkRevocations(ctx, rdb) + } + } +} + +func (m *Manager) checkRevocations(ctx context.Context, rdb *redis.Client) { + m.mu.RLock() + ids := make([]string, 0, len(m.sessions)) + for id := range m.sessions { + ids = append(ids, id) + } + m.mu.RUnlock() + + for _, id := range ids { + raw, err := rdb.Get(ctx, "session:"+id).Result() + if err != nil { + continue + } + var data struct { + State string `json:"state"` + } + if json.Unmarshal([]byte(raw), &data) != nil { + continue + } + if data.State == "revoked" { + log.Printf("relay: session %s revoked - notifying connections", id) + m.broadcast(id, revokedMsg) + } + } +} + +func (m *Manager) broadcast(sessionID string, msg []byte) { + m.mu.Lock() + sess, ok := m.sessions[sessionID] + m.mu.RUnlock() + if !ok { + return + } + + sess.mu.RLock() + defer sess.mu.RUnlock() + + if sess.Host != nil { + safeSend(sess.Host.SendCh, msg) + } + for _, c := range sess.Clients { + safeSend(c.SendCh, msg) + } +} + +func safeSend(ch chan []byte, msg []byte) { + defer func () { recover() } () + select { + case ch <- msg: + default: + + } +} + +var revokedMsg = func () []byte { + b, _ := json.Marshal(map[string]interface{}{ + "v": 1, + "type": "SESSION_REVOKED", + "payload": map[string]string{"reason": "host_requested"}, + }) + return b +} () \ No newline at end of file diff --git a/test-runner.js b/test-runner.js deleted file mode 100644 index 74a2233..0000000 --- a/test-runner.js +++ /dev/null @@ -1,17 +0,0 @@ -const { execSync } = require('child_process'); - -try { - console.log('Running Dashboard tests...\n'); - const output = execSync('npx vitest run tests/unit/mobile-client/Dashboard.test.tsx --reporter=verbose --no-coverage', { - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'pipe'], - maxBuffer: 10 * 1024 * 1024 - }); - console.log(output); - console.log('\nβœ“ Tests completed successfully'); -} catch (error) { - if (error.stdout) console.log('STDOUT:\n', error.stdout); - if (error.stderr) console.log('STDERR:\n', error.stderr); - console.log('\nβœ— Tests failed with exit code:', error.status); - process.exit(error.status || 1); -} diff --git a/tsconfig.json b/tsconfig.json index 78c55fd..650f2ca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,5 +19,7 @@ "packages/vscode-extension/src/**/*", "packages/mobile-client/src/**/*" ], - "exclude": ["node_modules/@testing-library/react-native/src"] + "exclude": ["node_modules/@testing-library/react-native/src"], + "files": [], + "references": [{ "path": "./packages/protocol" }, { "path": "./packages/vscode-extension" }] } diff --git a/vitest.config.ts b/vitest.config.ts index cf0be84..37885a9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -9,31 +9,21 @@ export default defineConfig({ test: { environment: 'node', include: [ - 'tests/unit/protocol/**/*.test.ts', - 'tests/unit/relay-server/**/*.test.ts', - 'tests/unit/vscode-extension/**/*.test.ts', - 'tests/unit/mobile-client/**/*.test.tsx', - 'tests/unit/mobile-client/**/*.test.ts', - 'tests/integration/**/*.test.ts', - 'tests/property/**/*.test.ts', - 'tests/performance/**/*.test.ts' + 'packages/**/*.test.ts', + 'packages/**/*.test.tsx' ], exclude: [ - 'tests/unit/mobile-client/components.test.tsx' + '**/node_modules/**', + '**/dist/**' ], - setupFiles: ['./tests/setup/test-setup.ts', './tests/setup/react-native-testing-setup.ts'], coverage: { provider: 'v8', reporter: ['text', 'html', 'json', 'lcov'], include: [ 'packages/protocol/src/**/*.ts', - 'packages/relay-server/src/**/*.ts', 'packages/vscode-extension/src/**/*.ts', - 'packages/mobile-client/src/**/*.ts', - 'packages/mobile-client/src/**/*.tsx' ], exclude: [ - 'tests/**', '**/*.test.ts', '**/*.test.tsx', '**/dist/**', @@ -50,35 +40,11 @@ export default defineConfig({ }, globals: true, testTimeout: 10000, - // Disable source map resolution to prevent loading TypeScript sources - sourcemap: false, - // Use jsdom for React Native component tests - environmentMatchGlobs: [ - ['tests/unit/mobile-client/**', 'jsdom'] - ], - // Transform node_modules that need transpilation - server: { - deps: { - inline: [ - 'react-native', - '@testing-library/react-native', - ], - // Don't externalize @testing-library/react-native so it gets transformed - external: [] - } - }, - // Explicitly exclude node_modules from transformation except those we inline - exclude: [ - ...['node_modules/**'], - 'tests/unit/mobile-client/components.test.tsx' - ] + sourcemap: false }, resolve: { alias: { '@codelink/protocol': path.resolve(__dirname, './packages/protocol/src'), - // Alias react-native to our mock for testing - 'react-native': path.resolve(__dirname, './tests/setup/react-native-mock.ts'), - // Resolve React from root node_modules (monorepo setup) 'react': path.resolve(__dirname, './node_modules/react'), 'react-dom': path.resolve(__dirname, './node_modules/react-dom'), },