diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..85105be --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,52 @@ +name: CI + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + branches: [main] + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install + + - name: Lint + run: bun run lint + + - name: Format check + run: bun run format:check + + - name: Type check + run: bun run typecheck + + - name: Build all platforms + run: | + bun run build:linux + bun run build:linux-arm + bun run build:macos + bun run build:macos-arm + bun run build:windows + + - name: Upload release assets + if: startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v2 + with: + files: | + dist/switch-craft-linux + dist/switch-craft-linux-arm64 + dist/switch-craft-macos + dist/switch-craft-macos-arm64 + dist/switch-craft-win.exe diff --git a/.gitignore b/.gitignore index 485dee6..69ca682 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .idea +node_modules/ +dist/ +*.tsbuildinfo diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..785de29 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "tabWidth": 2, + "printWidth": 100 +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..19b406c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,74 @@ +# Contributing + +## Development + +```bash +# Install dependencies +bun install + +# Build +bun run build + +# Build local binary +bun run build:local + +# Lint +bun run lint + +# Format +bun run format +``` + +## Generating the Demo GIF + +The demo GIF is created using [VHS](https://github.com/charmbracelet/vhs), a terminal recording tool. + +### Prerequisites + +1. Install VHS: + ```bash + go install github.com/charmbracelet/vhs@latest + ``` + +2. Install ttyd with nerd font support (for icons): + ```bash + # Install dependencies + sudo apt-get install build-essential cmake git libjson-c-dev libwebsockets-dev + + # Clone and build ttyd-nerd-font + git clone https://github.com/metorm/ttyd-nerd-font.git + cd ttyd-nerd-font && mkdir build && cd build + cmake .. && make + sudo make install + ``` + +3. Install ffmpeg: + ```bash + sudo apt install ffmpeg + ``` + +### Recording the Demo + +1. Build the project first: + ```bash + bun run build:local + ``` + +2. Run VHS with the tape file: + ```bash + vhs demo.tape + ``` + +This generates `img/demo.gif` from the instructions in `demo.tape`. + +### Customizing the Demo + +Edit `demo.tape` to change the recording. The tape file uses a test config (`config.example.json`) so your personal projects aren't shown. + +Key VHS commands: +- `Type "text"` - Types text +- `Enter` - Press enter +- `Down` / `Up` - Arrow keys +- `Ctrl+o` - Ctrl+O keypress +- `Sleep 500ms` - Pause recording +- `Hide` / `Show` - Hide/show commands from recording diff --git a/README.md b/README.md index f5b1f6d..304371d 100644 --- a/README.md +++ b/README.md @@ -1,118 +1,193 @@ # switch-craft -Effortlessly navigate between projects with a single command. +A cross-shell project switcher with environment management. Effortlessly navigate between projects with a single command. ![demo](img/demo.gif) -## Oh My Zsh +## Features -1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`) +- **Fuzzy project search** - Find projects quickly with fuzzy matching +- **Environment management** - Automatically set kubectx, gcloud, AWS profiles, and more +- **Cross-shell support** - Works with Bash, Zsh, Fish, and PowerShell +- **Interactive selector** - Built-in fuzzy finder for project selection +- **Colorful output** - Rainbow ASCII banners and colored info tables +- **Command preview** - Press Ctrl+O to preview commands before switching +- **Reset command** - Clear all environment settings with one command - ```sh - git clone https://github.com/amerryma/switch-craft ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/switch-craft - ``` +## Installation -2. Add the plugin to the list of plugins for Oh My Zsh to load (inside `~/.zshrc`): +### Prerequisites - ```sh - plugins=( - # other plugins... - switch-craft - ) - ``` +- [Bun](https://bun.sh/) runtime -3. Start a new terminal session. +### Quick Install -## Set Project Alias +```bash +# Clone the repository +git clone https://github.com/amerryma/switch-craft ~/Projects/switch-craft +cd ~/Projects/switch-craft -The arguments are positional. Use quotes if you need to include spaces. If you need -to skip an argument, use "false". - -```sh -alias cdp='switch_craft ' +# Install and build +bun install +bun run build:local ``` -To switch the default project dir of `~/Projects`, you can set the `SWITCH_CRAFT_PROJECTS_DIR` environment variable. - -```sh -export SWITCH_CRAFT_PROJECTS_DIR=~/my-projects +### Shell Integration -# or per alias +Add to your shell profile: -alias cdp='SWITCH_CRAFT_PROJECTS_DIR=~/my-projects switch_craft <..args>' +**Bash/Zsh:** +```bash +sc() { eval "$(~/Projects/switch-craft/dist/switch-craft go sh "$@")"; } +scx() { eval "$(~/Projects/switch-craft/dist/switch-craft select sh)"; } +scc() { eval "$(~/Projects/switch-craft/dist/switch-craft reset sh)"; } +alias scl="~/Projects/switch-craft/dist/switch-craft list" ``` -## Supported Arguments +**Fish:** +```fish +function sc; ~/Projects/switch-craft/dist/switch-craft go fish $argv | source; end +function scx; ~/Projects/switch-craft/dist/switch-craft select fish | source; end +function scc; ~/Projects/switch-craft/dist/switch-craft reset fish | source; end +alias scl="~/Projects/switch-craft/dist/switch-craft list" +``` -### Project Display Name +**PowerShell:** +```powershell +function sc { (& ~/Projects/switch-craft/dist/switch-craft go pwsh $args) | Invoke-Expression } +function scx { (& ~/Projects/switch-craft/dist/switch-craft select pwsh) | Invoke-Expression } +function scc { (& ~/Projects/switch-craft/dist/switch-craft reset pwsh) | Invoke-Expression } +function scl { & ~/Projects/switch-craft/dist/switch-craft list } +``` -This is the name that will be displayed in the prompt. It can be anything you want. +## Configuration + +Create `~/.config/switch-craft/config.json`: + +```json +{ + "projectsDir": "/home/user/projects", + "icons": { + "k8s": "☸️", + "gcp": "☁️", + "aws": "󰸏", + "venv": "" + }, + "projects": [ + { + "name": "acme", + "path": "clients/acme", + "kubectx": "acme-prod", + "aws": "acme-prod" + }, + { + "name": "nexus", + "path": "clients/nexus", + "gcloud": "nexus-main" + }, + { + "name": "internal", + "path": "internal/tools" + } + ] +} +``` -### Project Name +### Custom Icons -This is the name of the directory that contains the project. It is relative to the `SWITCH_CRAFT_PROJECTS_DIR` -environment variable. +The optional `icons` field allows you to customize the display labels for different service types. Icons will be shown in the interactive selector alongside the text labels. If not specified, only text labels (e.g., `k8s:`, `gcp:`) are shown. -### Kubectx +### Environment Variables -If you have [kubectx](https://github.com/ahmetb/kubectx/) installed, you can set the third argument to the name of the -context you want to switch to a context name. You can list the available contexts with: +| Variable | Description | +|----------|-------------| +| `SWITCH_CRAFT_CONFIG` | Path to config file (default: `~/.config/switch-craft/config.json`) | +| `SWITCH_CRAFT_PROJECTS_DIR` | Override base projects directory | -```sh -kubectx -``` +## Usage -### Gcloud +### Commands -If you have [gcloud](https://cloud.google.com/sdk/gcloud) installed, you can set the fourth argument to the name of the -gcloud configuration you want to switch to. You can list the available configurations with: +| Command | Description | +|---------|-------------| +| `sc ` | Switch to project with full environment setup | +| `scx` | Interactive fuzzy project selector | +| `scc` | Reset/clear environment and go to projects dir | +| `scl` | List all configured projects | -```sh -gcloud config configurations list -``` +### Interactive Controls -### Virtualenv +| Key | Action | +|-----|--------| +| `↑/↓` | Navigate project list | +| `Enter` | Select project | +| `Ctrl+O` | Toggle command preview | +| `Esc` | Cancel | -If you have a virtualenv in your project, you can set the fifth argument to the relative path within the projects dir to -automatically activate the virtualenv that is a sibling of the current project. This could be useful for running python -applications while in a different directory. +### Examples -This requires the zsh plugin called `autoswitch_virtualenv` which is -found [here](https://github.com/MichaelAquilina/zsh-autoswitch-virtualenv). +```bash +# Switch to a project (fuzzy matching works!) +sc acme +sc nex # matches "nexus" -### AWS +# Interactive selection +scx -If you have [aws](https://aws.amazon.com/cli/) installed, you can set the sixth argument to the name of the aws profile -you want to switch to. You can list the available profiles with: +# Reset environment +scc -```sh -aws configure list-profiles +# List projects +scl ``` -### Azure - -If you have [az](https://learn.microsoft.com/en-us/cli/azure/) installed, you can set the seventh argument to the name -of the azure profile you want to switch to. You can list the available profiles with: +### Project Properties -```sh -az account list --output table -``` +| Property | Description | +|----------|-------------| +| `name` | Project identifier (used for fuzzy search) | +| `path` | Directory path (relative to projectsDir or absolute) | +| `kubectx` | Kubernetes context to activate | +| `gcloud` | Google Cloud configuration name | +| `aws` | AWS profile name | +| `azure` | Azure account | +| `venv` | Python virtualenv path to activate | +| `env` | Custom environment variables to export | -## Additional Dependencies +## CLI Reference -### figlet & lolcat +``` +switch-craft [options] [args] + +Commands: + go [name] Switch to project (interactive if no name) + reset Reset environment and go to projects dir + path [name] Print project path (interactive if no name) + list List all configured projects + select Interactive fuzzy project selector + +Shells: + sh Bash/Zsh compatible output + fish Fish shell output + pwsh PowerShell output + +Options: + --no-env Skip environment variable exports + -h, --help Show help + -v, --version Show version +``` -For more fun, you can install [figlet](http://www.figlet.org/) and [lolcat](https://github.com/busyloop/lolcat) +## Recommended Tools -You will need to install `JS Stick Letters` font. +- [Spaceship Prompt](https://github.com/spaceship-prompt/spaceship-prompt) - Shows cloud/k8s context in prompt +- [kubectx](https://github.com/ahmetb/kubectx) - Kubernetes context switcher +- [gcloud CLI](https://cloud.google.com/sdk/gcloud) - Google Cloud SDK +- [AWS CLI](https://aws.amazon.com/cli/) - AWS command line interface -```sh -sudo curl -o /usr/share/figlet/JS\ Stick\ Letters.flf https://raw.githubusercontent.com/xero/figlet-fonts/master/JS%20Stick%20Letters.flf -``` +## Contributing -![clean](img/clean.png) +See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and how to generate the demo GIF. -### Spaceship Prompt +## License -This plugin works well with the [Spaceship Prompt](https://github.com/spaceship-prompt/spaceship-prompt) -as it shows the different components in the prompt. +MIT diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..fdee063 --- /dev/null +++ b/bun.lock @@ -0,0 +1,656 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "switch-craft", + "dependencies": { + "fuse.js": "^7.0.0", + "ink": "^5.1.0", + "ink-text-input": "^6.0.0", + "react": "^18.3.1", + }, + "devDependencies": { + "@eslint/js": "^9.39.2", + "@types/bun": "^1.1.0", + "@types/react": "^18.3.0", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.0.1", + "prettier": "^3.7.4", + "typescript": "^5.3.0", + "typescript-eslint": "^8.50.0", + }, + }, + }, + "packages": { + "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.1.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw=="], + + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="], + + "@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="], + + "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], + + "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], + + "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], + + "@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], + + "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], + + "@types/react": ["@types/react@18.3.27", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.50.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/type-utils": "8.50.0", "@typescript-eslint/utils": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.50.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.50.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.50.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.50.0", "@typescript-eslint/types": "^8.50.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0" } }, "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.50.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/utils": "8.50.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.50.0", "", {}, "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.50.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.50.0", "@typescript-eslint/tsconfig-utils": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/visitor-keys": "8.50.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.50.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.50.0", "", { "dependencies": { "@typescript-eslint/types": "8.50.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.9", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001760", "", {}, "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], + + "cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="], + + "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + + "code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="], + + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-iterator-helpers": ["es-iterator-helpers@1.2.2", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.1", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "safe-array-concat": "^1.1.3" } }, "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "es-toolkit": ["es-toolkit@1.43.0", "", {}, "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], + + "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "fuse.js": ["fuse.js@7.1.0", "", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], + + "ink": ["ink@5.2.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.1.3", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.22.0", "indent-string": "^5.0.0", "is-in-ci": "^1.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.29.0", "scheduler": "^0.23.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0", "react-devtools-core": "^4.19.1" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg=="], + + "ink-text-input": ["ink-text-input@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" }, "peerDependencies": { "ink": ">=5", "react": ">=18" } }, "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-in-ci": ["is-in-ci@1.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + + "react-devtools-core": ["react-devtools-core@7.0.1", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-reconciler": ["react-reconciler@0.29.2", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="], + + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + + "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-eslint": ["typescript-eslint@8.50.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.50.0", "@typescript-eslint/parser": "8.50.0", "@typescript-eslint/typescript-estree": "8.50.0", "@typescript-eslint/utils": "8.50.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + + "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], + + "zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + + "ink/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "ink-text-input/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + + "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + } +} diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..76e3000 --- /dev/null +++ b/config.example.json @@ -0,0 +1,118 @@ +{ + "projectsDir": "/home/dev/projects", + "icons": { + "k8s": "☸️", + "gcp": "☁️", + "aws": "󰸏", + "azure": "󰠅", + "venv": "" + }, + "projects": [ + { + "name": "acme", + "path": "clients/acme", + "kubectx": "acme-prod", + "aws": "acme-prod" + }, + { + "name": "nexus", + "path": "clients/nexus", + "kubectx": "nexus-staging", + "gcloud": "nexus-main", + "aws": "nexus" + }, + { + "name": "titan", + "path": "clients/titan", + "kubectx": "titan-prod", + "aws": "titan-prod", + "venv": "/home/dev/.venvs/titan" + }, + { + "name": "horizon", + "path": "clients/horizon", + "gcloud": "horizon" + }, + { + "name": "stellar", + "path": "clients/stellar", + "aws": "stellar" + }, + { + "name": "pulse", + "path": "clients/pulse", + "kubectx": "pulse-staging" + }, + { + "name": "forge", + "path": "clients/forge", + "kubectx": "forge-prod", + "aws": "forge-prod" + }, + { + "name": "ember", + "path": "clients/ember", + "azure": "ember-prod" + }, + { + "name": "aurora", + "path": "clients/aurora" + }, + { + "name": "vertex", + "path": "clients/vertex", + "aws": "vertex-dev" + }, + { + "name": "catalyst", + "path": "clients/catalyst", + "venv": "/home/dev/.venvs/catalyst" + }, + { + "name": "pinnacle", + "path": "clients/pinnacle", + "kubectx": "pinnacle-prod" + }, + { + "name": "zenith", + "path": "clients/zenith", + "kubectx": "zenith-prod", + "gcloud": "zenith", + "aws": "zenith" + }, + { + "name": "radiant", + "path": "clients/radiant" + }, + { + "name": "summit", + "path": "clients/summit", + "gcloud": "summit" + }, + { + "name": "onyx", + "path": "clients/onyx", + "aws": "onyx", + "venv": "/home/dev/.venvs/onyx" + }, + { + "name": "internal-tools", + "path": "internal/tools" + }, + { + "name": "devops", + "path": "internal/devops", + "aws": "master" + }, + { + "name": "ml-pipeline", + "path": "internal/ml", + "gcloud": "ml-research", + "venv": "/home/dev/.venvs/ml" + }, + { + "name": "sandbox", + "path": "sandbox" + } + ] +} diff --git a/demo.tape b/demo.tape new file mode 100644 index 0000000..3a64090 --- /dev/null +++ b/demo.tape @@ -0,0 +1,46 @@ +# switch-craft demo +Output img/demo.gif +Set Framerate 60 + +Set Shell bash +Set FontFamily "FiraCode Nerd Font" +Set FontSize 16 +Set Width 1000 +Set Height 600 +Set WindowBar Colorful +Set WindowBarSize 40 +Set BorderRadius 8 +Set Margin 20 +Set MarginFill "#1a1a2e" +Set Padding 15 +Set TypingSpeed 75ms + +# Setup and launch (hidden) +Hide +Type "cd ~/Projects/switch-craft" +Enter +Type "export SWITCH_CRAFT_CONFIG=./config.example.json" +Enter +Type "./dist/switch-craft select sh" +Enter +Sleep 500ms +Show + +# Let the UI settle +Sleep 1s + +# Navigate down +Down +Sleep 500ms +Down +Sleep 500ms +Down +Sleep 500ms + +# Type to filter +Type "titan" +Sleep 1s + +# Show command preview with Ctrl+O +Ctrl+o +Sleep 4s diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..56cd60b --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,40 @@ +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import react from 'eslint-plugin-react'; +import reactHooks from 'eslint-plugin-react-hooks'; +import prettier from 'eslint-config-prettier'; + +export default tseslint.config( + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{ts,tsx}'], + plugins: { + react, + 'react-hooks': reactHooks, + }, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + settings: { + react: { + version: '18.3', + }, + }, + rules: { + ...react.configs.recommended.rules, + ...reactHooks.configs.recommended.rules, + 'react/react-in-jsx-scope': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + 'react-hooks/purity': 'off', // False positives for Date.now() in callbacks + }, + }, + prettier, + { + ignores: ['dist/**', 'node_modules/**'], + } +); diff --git a/img/clean.png b/img/clean.png deleted file mode 100644 index a243a82..0000000 Binary files a/img/clean.png and /dev/null differ diff --git a/img/demo.gif b/img/demo.gif index fe5f141..1ae4246 100644 Binary files a/img/demo.gif and b/img/demo.gif differ diff --git a/img/demo.png b/img/demo.png deleted file mode 100644 index 08aafad..0000000 Binary files a/img/demo.png and /dev/null differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..26a47a9 --- /dev/null +++ b/package.json @@ -0,0 +1,62 @@ +{ + "name": "switch-craft", + "version": "2.0.0", + "description": "Cross-shell CLI tool for switching between development projects with environment management", + "type": "module", + "main": "dist/cli.js", + "bin": { + "switch-craft": "dist/cli.js" + }, + "scripts": { + "build": "bun build ./src/cli.tsx --outdir ./dist --target node", + "build:bin": "bun run build:linux && bun run build:macos && bun run build:windows", + "build:linux": "bun build --compile --target=bun-linux-x64 ./src/cli.tsx --outfile dist/switch-craft-linux", + "build:linux-arm": "bun build --compile --target=bun-linux-arm64 ./src/cli.tsx --outfile dist/switch-craft-linux-arm64", + "build:macos": "bun build --compile --target=bun-darwin-x64 ./src/cli.tsx --outfile dist/switch-craft-macos", + "build:macos-arm": "bun build --compile --target=bun-darwin-arm64 ./src/cli.tsx --outfile dist/switch-craft-macos-arm64", + "build:windows": "bun build --compile --target=bun-windows-x64 ./src/cli.tsx --outfile dist/switch-craft-win.exe", + "build:local": "bun build --compile ./src/cli.tsx --outfile dist/switch-craft", + "dev": "bun --watch ./src/cli.tsx", + "start": "bun ./src/cli.tsx", + "clean": "rm -rf dist", + "typecheck": "tsc --noEmit", + "lint": "eslint src/", + "lint:fix": "eslint src/ --fix", + "format": "prettier --write src/", + "format:check": "prettier --check src/" + }, + "keywords": [ + "cli", + "shell", + "project", + "switcher", + "developer", + "productivity", + "fuzzy", + "search" + ], + "author": "Aaron Merryman", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/amerryma/switch-craft" + }, + "dependencies": { + "fuse.js": "^7.0.0", + "ink": "^5.1.0", + "ink-text-input": "^6.0.0", + "react": "^18.3.1" + }, + "devDependencies": { + "@eslint/js": "^9.39.2", + "@types/bun": "^1.1.0", + "@types/react": "^18.3.0", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.0.1", + "prettier": "^3.7.4", + "typescript": "^5.3.0", + "typescript-eslint": "^8.50.0" + } +} diff --git a/src/cli.tsx b/src/cli.tsx new file mode 100644 index 0000000..153a00c --- /dev/null +++ b/src/cli.tsx @@ -0,0 +1,278 @@ +#!/usr/bin/env bun + +// Force color output when stderr is a TTY (needed because we render to stderr) +if (process.stderr.isTTY && !process.env.FORCE_COLOR) { + process.env.FORCE_COLOR = '1'; +} + +import React from 'react'; +import { render } from 'ink'; +import { parseArgs } from 'util'; +import { existsSync, statSync } from 'node:fs'; +import { loadConfig, resolveProjectPath } from './config.js'; +import { findProject, validatePathExists, error } from './utils.js'; +import { emit, emitReset } from './emitters/index.js'; +import { ProjectSelector } from './components/ProjectSelector.js'; +import type { ShellType, Project } from './types.js'; + +const VERSION = '3.0.0'; + +const HELP = ` +switch-craft - Cross-shell project switcher with environment management + +USAGE: + switch-craft [options] [args] + +COMMANDS: + go [name] Switch to project (interactive if no name) + reset Reset environment and go to projects dir + path [name] Print project path (interactive if no name) + list List all configured projects + select Interactive fuzzy project selector + +SHELLS: + sh Bash/Zsh compatible output + fish Fish shell output + pwsh PowerShell output + +OPTIONS: + --no-env Skip environment variable exports + -h, --help Show this help message + -v, --version Show version + +CONFIGURATION: + Default config: ~/.config/switch-craft/config.json + + Environment variables: + SWITCH_CRAFT_CONFIG Path to config file + SWITCH_CRAFT_PROJECTS_DIR Override base projects directory + +SHELL INTEGRATION (add to your shell profile): + + # Bash/Zsh + sc() { eval "$(switch-craft go sh "$@")"; } + scx() { eval "$(switch-craft select sh)"; } + scc() { eval "$(switch-craft reset sh)"; } + alias scl="switch-craft list" + + # Fish + function sc; switch-craft go fish $argv | source; end + function scx; switch-craft select fish | source; end + function scc; switch-craft reset fish | source; end + alias scl="switch-craft list" + + # PowerShell + function sc { Invoke-Expression (& switch-craft go pwsh $args) } + function scx { Invoke-Expression (& switch-craft select pwsh) } + function scc { Invoke-Expression (& switch-craft reset pwsh) } + function scl { & switch-craft list } + +EXAMPLES: + switch-craft go sh myproject + switch-craft reset sh + switch-craft list + switch-craft select sh +`; + +const VALID_SHELLS: ShellType[] = ['sh', 'fish', 'pwsh']; + +function showHelp(): void { + console.log(HELP.trim()); + process.exit(0); +} + +function showVersion(): void { + console.log(`switch-craft ${VERSION}`); + process.exit(0); +} + +function validateShell(shell: string | undefined): ShellType { + if (!shell) { + error('Missing shell type. Must be one of: sh, fish, pwsh'); + } + if (!VALID_SHELLS.includes(shell as ShellType)) { + error(`Invalid shell type "${shell}". Must be one of: sh, fish, pwsh`); + } + return shell as ShellType; +} + +// Non-interactive commands +function listCommand(): void { + const config = loadConfig(); + if (config.projects.length === 0) { + console.log('No projects configured.'); + return; + } + for (const project of config.projects) { + const fullPath = resolveProjectPath(config.projectsDir, project.path); + let status = ''; + if (!existsSync(fullPath)) { + status = ' (path not found)'; + } else { + try { + const stat = statSync(fullPath); + if (!stat.isDirectory()) status = ' (not a directory)'; + } catch { + status = ' (path not found)'; + } + } + console.log(`${project.name}${status}`); + } +} + +function pathCommand(projectName: string): void { + const config = loadConfig(); + const project = findProject(config.projects, projectName); + if (!project) { + const available = config.projects.map((p) => p.name).join(', '); + error(`Project "${projectName}" not found. Available: ${available}`); + } + const fullPath = resolveProjectPath(config.projectsDir, project.path); + validatePathExists(fullPath, project.name); + console.log(fullPath); +} + +function emitCommand(shell: ShellType, projectName: string, noEnv: boolean): void { + const config = loadConfig(); + const project = findProject(config.projects, projectName); + if (!project) { + const available = config.projects.map((p) => p.name).join(', '); + error(`Project "${projectName}" not found. Available: ${available}`); + } + const fullPath = resolveProjectPath(config.projectsDir, project.path); + validatePathExists(fullPath, project.name); + const output = emit(shell, { project, fullPath, noEnv }); + console.log(output); +} + +function resetCommand(shell: ShellType): void { + const config = loadConfig(); + const output = emitReset(shell, config.projectsDir); + console.log(output); +} + +// Interactive mode with Ink +async function interactiveSelect( + mode: 'emit' | 'path', + shell?: ShellType, + noEnv?: boolean +): Promise { + if (!process.stdin.isTTY) { + error('Interactive mode requires a TTY. Use "switch-craft list" to see projects.'); + } + + const config = loadConfig(); + if (config.projects.length === 0) { + error('No projects configured.'); + } + + return new Promise((resolve) => { + const handleSelect = (project: Project) => { + // Clear the Ink output + instance.clear(); + instance.unmount(); + + const fullPath = resolveProjectPath(config.projectsDir, project.path); + validatePathExists(fullPath, project.name); + + if (mode === 'path') { + console.log(fullPath); + } else if (mode === 'emit' && shell) { + const output = emit(shell, { project, fullPath, noEnv: noEnv ?? false }); + console.log(output); + } + + resolve(); + }; + + const handleCancel = () => { + instance.clear(); + instance.unmount(); + process.exit(130); + }; + + const instance = render( + , + { + stdout: process.stderr, // Render UI to stderr, keep stdout for shell commands + patchConsole: false, + } + ); + }); +} + +async function main(): Promise { + const { values, positionals } = parseArgs({ + args: process.argv.slice(2), + options: { + 'no-env': { type: 'boolean', default: false }, + help: { type: 'boolean', short: 'h', default: false }, + version: { type: 'boolean', short: 'v', default: false }, + }, + allowPositionals: true, + }); + + if (values.help) showHelp(); + if (values.version) showVersion(); + + const command = positionals[0]; + + if (!command) { + showHelp(); + } + + switch (command) { + case 'go': { + const shell = validateShell(positionals[1]); + const projectName = positionals[2]; + if (!projectName) { + await interactiveSelect('emit', shell, values['no-env']); + } else { + emitCommand(shell, projectName, values['no-env'] ?? false); + } + break; + } + + case 'reset': { + const shell = validateShell(positionals[1]); + resetCommand(shell); + break; + } + + case 'path': { + const projectName = positionals[1]; + if (!projectName) { + await interactiveSelect('path'); + } else { + pathCommand(projectName); + } + break; + } + + case 'list': { + listCommand(); + break; + } + + case 'select': { + const shell = validateShell(positionals[1]); + await interactiveSelect('emit', shell, values['no-env']); + break; + } + + default: + error(`Unknown command: ${command}`); + } +} + +main().catch((err) => { + console.error(`switch-craft: ${err.message}`); + process.exit(1); +}); diff --git a/src/components/ProjectSelector.tsx b/src/components/ProjectSelector.tsx new file mode 100644 index 0000000..3d15db6 --- /dev/null +++ b/src/components/ProjectSelector.tsx @@ -0,0 +1,378 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Box, Text, useInput, useApp } from 'ink'; +import TextInput from 'ink-text-input'; +import Fuse from 'fuse.js'; +import type { Project, Icons } from '../types.js'; +import { resolveProjectPath } from '../config.js'; +import { getServiceColor as getServiceHexColor } from '../emitters/services/index.js'; + +interface ProjectSelectorProps { + projects: Project[]; + onSelect: (project: Project) => void; + onCancel: () => void; + projectsDir: string; + noEnv?: boolean; + icons?: Icons; +} + +// True color (24-bit) ANSI codes for vibrant colors +const COLORS = { + reset: '\x1b[0m', + bold: '\x1b[1m', + dim: '\x1b[2m', +}; + +const RAINBOW_COLORS = [ + '\x1b[38;2;255;50;50m', // red + '\x1b[38;2;255;150;0m', // orange + '\x1b[38;2;255;255;0m', // yellow + '\x1b[38;2;50;255;100m', // green + '\x1b[38;2;0;255;255m', // cyan + '\x1b[38;2;80;150;255m', // blue + '\x1b[38;2;180;100;255m', // purple + '\x1b[38;2;255;100;255m', // magenta +] as const; + +// Convert hex color to ANSI escape code +function hexToAnsi(hex: string): string { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + return `\x1b[38;2;${r};${g};${b}m`; +} + +// Get ANSI color for a service (converts hex from registry to ANSI) +function getServiceColor(service: string): string { + return hexToAnsi(getServiceHexColor(service)); +} + +// Rainbow from center outward (colors flow outward) +function rainbowText(text: string, offset: number = 0): string { + const mid = Math.floor(text.length / 2); + return ( + text + .split('') + .map((char, i) => { + // Distance from center + const dist = Math.abs(i - mid); + // Subtract offset so colors flow outward from center + const colorIndex = + (((dist - offset) % RAINBOW_COLORS.length) + RAINBOW_COLORS.length) % + RAINBOW_COLORS.length; + return `${RAINBOW_COLORS[colorIndex]}${char}`; + }) + .join('') + COLORS.reset + ); +} + +// Rotate string by n positions (positive = left rotation) +function rotateString(str: string, n: number): string { + const len = str.length; + const shift = ((n % len) + len) % len; + return str.slice(shift) + str.slice(0, shift); +} + +// Generate preview of commands that will run (only show what will be SET, not what will be cleared) +function generatePreview(project: Project, projectsDir: string, noEnv: boolean): string[] { + const commands: string[] = []; + const fullPath = resolveProjectPath(projectsDir, project.path); + + if (!noEnv) { + // Only show commands that SET something, not clear/unset commands + + // Kubectx + if (project.kubectx) { + commands.push(`kubectx ${project.kubectx}`); + } + + // GCloud + if (project.gcloud) { + commands.push(`gcloud config activate ${project.gcloud}`); + } + + // AWS + if (project.aws) { + commands.push(`export AWS_PROFILE=${project.aws}`); + } + + // Azure + if (project.azure) { + commands.push(`# Azure account: ${project.azure}`); + } + + // Custom env vars + if (project.env) { + for (const [key, value] of Object.entries(project.env)) { + commands.push(`export ${key}=${value}`); + } + } + + // Virtual environment + if (project.venv) { + commands.push(`source ${project.venv}/bin/activate`); + } + } + + // Change directory + commands.push(`cd ${fullPath}`); + + return commands; +} + +export function ProjectSelector({ + projects, + onSelect, + onCancel, + projectsDir, + noEnv, + icons, +}: ProjectSelectorProps) { + const { exit } = useApp(); + const [query, setQuery] = useState(''); + const [selectedIndex, setSelectedIndex] = useState(0); + const [scrollOffset, setScrollOffset] = useState(0); + const [colorOffset, setColorOffset] = useState(0); + const [titleRotation, setTitleRotation] = useState(0); + const [titleDirection, setTitleDirection] = useState<'forward' | 'backward' | 'pause'>('forward'); + const [showPreview, setShowPreview] = useState(false); + const lastCtrlAction = useRef<{ char: string; time: number }>({ char: '', time: 0 }); + const pauseCount = useRef(0); + + const TITLE = 'switch-craft'; + const FULL_LENGTH = TITLE.length; // 12 - full rotation back to start + const PAUSE_FRAMES = 20; // ~2 seconds at 100ms interval + + const fuse = new Fuse(projects, { + keys: ['name'], + threshold: 0.4, + includeScore: true, + }); + + const filteredProjects = query ? fuse.search(query).map((r) => r.item) : projects; + + const maxVisible = 10; + + // Calculate visible window with scrolling + const visibleProjects = filteredProjects.slice(scrollOffset, scrollOffset + maxVisible); + const hasMoreAbove = scrollOffset > 0; + const hasMoreBelow = scrollOffset + maxVisible < filteredProjects.length; + + // Animation effect - cycle colors and rotate title + useEffect(() => { + const interval = setInterval(() => { + setColorOffset((prev) => (prev + 1) % RAINBOW_COLORS.length); + + // Title rotation animation - full left, pause, full right, pause, loop + if (titleDirection === 'pause') { + pauseCount.current++; + if (pauseCount.current >= PAUSE_FRAMES) { + setTitleDirection(titleRotation >= FULL_LENGTH ? 'backward' : 'forward'); + pauseCount.current = 0; + } + } else if (titleDirection === 'forward') { + setTitleRotation((prev) => { + if (prev >= FULL_LENGTH) { + // Completed full rotation back to "switch-craft", pause + setTitleDirection('pause'); + return prev; + } + return prev + 1; + }); + } else if (titleDirection === 'backward') { + setTitleRotation((prev) => { + if (prev <= 0) { + // Back to start, pause + setTitleDirection('pause'); + return prev; + } + return prev - 1; + }); + } + }, 100); + return () => clearInterval(interval); + }, [titleDirection, titleRotation, FULL_LENGTH]); + + // Clean up characters added via Ctrl+key combinations + useEffect(() => { + const timeSince = Date.now() - lastCtrlAction.current.time; + const char = lastCtrlAction.current.char; + + // If a character was added via Ctrl+key within last 100ms, remove it + if (char && query.endsWith(char) && timeSince < 100) { + setQuery(query.slice(0, -char.length)); + lastCtrlAction.current = { char: '', time: 0 }; + } + }, [query]); + + // Reset selection and scroll when query changes + useEffect(() => { + setSelectedIndex(0); + setScrollOffset(0); + }, [query]); + + useInput((input, key) => { + if (key.escape || (key.ctrl && input === 'c')) { + onCancel(); + exit(); + return; + } + + if (key.ctrl && input === 'o') { + lastCtrlAction.current = { char: 'o', time: Date.now() }; + setShowPreview((prev) => !prev); + return; + } + + if (key.return) { + const selected = filteredProjects[selectedIndex]; + if (selected) { + onSelect(selected); + } + return; + } + + if (key.upArrow) { + if (selectedIndex > 0) { + const newIndex = selectedIndex - 1; + setSelectedIndex(newIndex); + // Scroll up if selection goes above visible window + if (newIndex < scrollOffset) { + setScrollOffset(newIndex); + } + } + return; + } + + if (key.downArrow) { + if (selectedIndex < filteredProjects.length - 1) { + const newIndex = selectedIndex + 1; + setSelectedIndex(newIndex); + // Scroll down if selection goes below visible window + if (newIndex >= scrollOffset + maxVisible) { + setScrollOffset(newIndex - maxVisible + 1); + } + } + return; + } + }); + + // Build header with rotating title and static rainbow (no pulse) + const rotatedTitle = rotateString(TITLE, titleRotation); + const staticRainbowTitle = rainbowText(rotatedTitle, 0); // offset=0 for static rainbow + + return ( + + {/* Rotating Header with static rainbow */} + + {`${COLORS.dim}>${COLORS.reset} ${staticRainbowTitle}`} + - Select a project + + + {/* Search input */} + + Search: + + + + {/* Project list - scrollable, no color unless selected, colored prefixes */} + + {/* Scroll indicator - above */} + {hasMoreAbove && ↑ {scrollOffset} more above} + + {visibleProjects.length === 0 ? ( + No matches found + ) : ( + visibleProjects.map((project, visibleIndex) => { + // Calculate actual index in filtered list + const actualIndex = scrollOffset + visibleIndex; + const isSelected = actualIndex === selectedIndex; + + // Build the line with ANSI codes + let line = ''; + if (isSelected) { + // Animated rainbow for selected item + const selector = rainbowText('>', colorOffset); + const name = rainbowText(project.name, colorOffset); + line += `${selector} ${COLORS.bold}${name}${COLORS.reset}`; + } else { + // No color for unselected + line += ` ${project.name}`; + } + + // Add metadata with service-specific colors and optional icons + const metaParts: string[] = []; + if (project.kubectx) { + const label = icons?.k8s ? `${icons.k8s} k8s` : 'k8s'; + metaParts.push( + `${getServiceColor('k8s')}${label}:${COLORS.reset} ${project.kubectx}` + ); + } + if (project.gcloud) { + const label = icons?.gcp ? `${icons.gcp} gcp` : 'gcp'; + metaParts.push(`${getServiceColor('gcp')}${label}:${COLORS.reset} ${project.gcloud}`); + } + if (project.aws) { + const label = icons?.aws ? `${icons.aws} aws` : 'aws'; + metaParts.push(`${getServiceColor('aws')}${label}:${COLORS.reset} ${project.aws}`); + } + if (project.azure) { + const label = icons?.azure ? `${icons.azure} azure` : 'azure'; + metaParts.push( + `${getServiceColor('azure')}${label}:${COLORS.reset} ${project.azure}` + ); + } + if (project.venv) { + const label = icons?.venv ? `${icons.venv} venv` : 'venv'; + metaParts.push(`${getServiceColor('venv')}${label}:${COLORS.reset} ${project.venv}`); + } + + if (metaParts.length > 0) { + line += ` [${metaParts.join('] [')}]`; + } + + return ( + + {line} + + ); + }) + )} + + {/* Scroll indicator - below */} + {hasMoreBelow && ( + ↓ {filteredProjects.length - scrollOffset - maxVisible} more below + )} + + + {/* Command Preview */} + {showPreview && filteredProjects[selectedIndex] && ( + + + Commands Preview: + + {generatePreview(filteredProjects[selectedIndex], projectsDir, noEnv ?? false).map( + (cmd, i) => ( + + {' '} + {cmd} + + ) + )} + + )} + + {/* Footer */} + + + [↑/↓] Navigate {' '} [Enter] Select {' '} [Ctrl+O] Preview {' '} [Esc] Cancel + + + + ); +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..44a94e8 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,179 @@ +import { readFileSync, existsSync } from 'node:fs'; +import { homedir } from 'node:os'; +import { join, isAbsolute, resolve } from 'node:path'; +import type { Config, Project, Icons } from './types.js'; + +const DEFAULT_CONFIG_PATH = join(homedir(), '.config', 'switch-craft', 'config.json'); + +export function getConfigPath(): string { + return process.env.SWITCH_CRAFT_CONFIG || DEFAULT_CONFIG_PATH; +} + +export function loadConfig(): Config { + const configPath = getConfigPath(); + + if (!existsSync(configPath)) { + throw new Error( + `Configuration file not found: ${configPath}\n` + + `Create a config file or set SWITCH_CRAFT_CONFIG environment variable.` + ); + } + + let rawConfig: unknown; + try { + const content = readFileSync(configPath, 'utf-8'); + rawConfig = JSON.parse(content); + } catch (err) { + if (err instanceof SyntaxError) { + throw new Error(`Invalid JSON in configuration file: ${configPath}`); + } + throw new Error(`Failed to read configuration file: ${configPath}`); + } + + return validateConfig(rawConfig, configPath); +} + +function validateConfig(raw: unknown, configPath: string): Config { + if (typeof raw !== 'object' || raw === null) { + throw new Error(`Configuration must be a JSON object: ${configPath}`); + } + + const obj = raw as Record; + + // Get projectsDir from config or environment + let projectsDir = + process.env.SWITCH_CRAFT_PROJECTS_DIR || + (typeof obj.projectsDir === 'string' ? obj.projectsDir : null); + + if (!projectsDir) { + throw new Error( + `Missing "projectsDir" in config or SWITCH_CRAFT_PROJECTS_DIR environment variable` + ); + } + + // Expand ~ to home directory + if (projectsDir.startsWith('~')) { + projectsDir = join(homedir(), projectsDir.slice(1)); + } + + // Ensure projectsDir is absolute + if (!isAbsolute(projectsDir)) { + projectsDir = resolve(projectsDir); + } + + // Validate projects array + if (!Array.isArray(obj.projects)) { + throw new Error(`Missing or invalid "projects" array in configuration`); + } + + const projects: Project[] = obj.projects.map((p, index) => validateProject(p, index)); + + // Parse optional icons + let icons: Icons | undefined; + if (obj.icons !== undefined) { + if (typeof obj.icons !== 'object' || obj.icons === null || Array.isArray(obj.icons)) { + throw new Error(`"icons" must be an object`); + } + icons = {}; + const iconsObj = obj.icons as Record; + const validKeys = ['k8s', 'gcp', 'aws', 'azure', 'venv', 'path']; + for (const key of validKeys) { + if (iconsObj[key] !== undefined) { + if (typeof iconsObj[key] !== 'string') { + throw new Error(`Icon "${key}" must be a string`); + } + (icons as Record)[key] = iconsObj[key] as string; + } + } + } + + return { projectsDir, projects, icons }; +} + +function validateProject(raw: unknown, index: number): Project { + if (typeof raw !== 'object' || raw === null) { + throw new Error(`Project at index ${index} must be an object`); + } + + const obj = raw as Record; + + if (typeof obj.name !== 'string' || obj.name.trim() === '') { + throw new Error(`Project at index ${index} must have a non-empty "name" string`); + } + + if (typeof obj.path !== 'string' || obj.path.trim() === '') { + throw new Error(`Project at index ${index} must have a non-empty "path" string`); + } + + const project: Project = { + name: obj.name.trim(), + path: obj.path.trim(), + }; + + // Optional fields + if (obj.env !== undefined) { + if (typeof obj.env !== 'object' || obj.env === null || Array.isArray(obj.env)) { + throw new Error(`Project "${project.name}": "env" must be an object`); + } + const env: Record = {}; + for (const [key, value] of Object.entries(obj.env as Record)) { + if (typeof value !== 'string') { + throw new Error(`Project "${project.name}": env value for "${key}" must be a string`); + } + env[key] = value; + } + project.env = env; + } + + if (obj.kubectx !== undefined) { + if (typeof obj.kubectx !== 'string') { + throw new Error(`Project "${project.name}": "kubectx" must be a string`); + } + project.kubectx = obj.kubectx; + } + + if (obj.gcloud !== undefined) { + if (typeof obj.gcloud !== 'string') { + throw new Error(`Project "${project.name}": "gcloud" must be a string`); + } + project.gcloud = obj.gcloud; + } + + if (obj.aws !== undefined) { + if (typeof obj.aws !== 'string') { + throw new Error(`Project "${project.name}": "aws" must be a string`); + } + project.aws = obj.aws; + } + + if (obj.azure !== undefined) { + if (typeof obj.azure !== 'string') { + throw new Error(`Project "${project.name}": "azure" must be a string`); + } + project.azure = obj.azure; + } + + if (obj.venv !== undefined) { + if (typeof obj.venv !== 'string') { + throw new Error(`Project "${project.name}": "venv" must be a string`); + } + project.venv = obj.venv; + } + + return project; +} + +export function resolveProjectPath(projectsDir: string, projectPath: string): string { + // If project path is absolute, use it directly + if (isAbsolute(projectPath)) { + return projectPath; + } + + // Expand ~ in project path + if (projectPath.startsWith('~')) { + return join(homedir(), projectPath.slice(1)); + } + + // Otherwise, join with projectsDir + return join(projectsDir, projectPath); +} diff --git a/src/emitters/ProjectEmitter.ts b/src/emitters/ProjectEmitter.ts new file mode 100644 index 0000000..f30d737 --- /dev/null +++ b/src/emitters/ProjectEmitter.ts @@ -0,0 +1,265 @@ +import type { Project, ShellType } from '../types.js'; +import { ShellAdapter } from './ShellAdapter.js'; +import { + KubernetesService, + GCloudService, + AWSService, + AzureService, + VenvService, + getServiceColor, +} from './services/index.js'; + +export interface EmitContext { + project: Project; + fullPath: string; + noEnv: boolean; +} + +export class ProjectEmitter { + private shell: ShellAdapter; + private shellType: ShellType; + + // Services + private k8s: KubernetesService; + private gcloud: GCloudService; + private aws: AWSService; + private azure: AzureService; + private venv: VenvService; + + constructor(shellType: ShellType) { + this.shellType = shellType; + this.shell = ShellAdapter.create(shellType); + + // Initialize services + this.k8s = new KubernetesService(this.shell); + this.gcloud = new GCloudService(this.shell); + this.aws = new AWSService(this.shell); + this.azure = new AzureService(this.shell); + this.venv = new VenvService(this.shell, shellType); + } + + emit(ctx: EmitContext): string { + const lines: string[] = []; + const { project, fullPath, noEnv } = ctx; + + // Banner + lines.push(...this.generateBanner(project.name)); + + // Info table + lines.push(...this.generateTable(ctx)); + + if (!noEnv) { + // Deactivate venv first (always) + lines.push(...this.venv.deactivate()); + + // Kubernetes + if (project.kubectx) { + lines.push(...this.k8s.activate(project.kubectx)); + } else { + lines.push(...this.k8s.deactivate()); + } + + // GCloud + if (project.gcloud) { + lines.push(...this.gcloud.activate(project.gcloud)); + } else { + lines.push(...this.gcloud.deactivate()); + } + + // AWS + if (project.aws) { + lines.push(...this.aws.activate(project.aws)); + } else { + lines.push(...this.aws.deactivate()); + } + + // Azure + lines.push(...this.azure.deactivate()); + + // Custom env vars + if (project.env) { + for (const [key, value] of Object.entries(project.env)) { + lines.push(this.shell.setEnv(key, value)); + } + } + + // Venv (activate after everything else) + if (project.venv) { + lines.push(...this.venv.activate(project.venv)); + } + } + + // Change directory last + lines.push(this.shell.cd(fullPath)); + + return lines.join('\n'); + } + + emitReset(projectsDir: string): string { + const lines: string[] = []; + + // Banner + lines.push(this.shell.echo('')); + lines.push(this.shell.echoColored(' RESET', 'yellow')); + lines.push(this.shell.echo('')); + + // Deactivate all services + lines.push(...this.venv.deactivate()); + lines.push(...this.k8s.deactivate()); + lines.push(...this.gcloud.deactivate()); + lines.push(...this.aws.deactivate()); + lines.push(...this.azure.deactivate()); + + // Go to projects dir + lines.push(this.shell.cd(projectsDir)); + + return lines.join('\n'); + } + + private generateBanner(name: string): string[] { + if (this.shellType === 'sh') { + return this.generateShBanner(name); + } + // Simpler banner for Fish/PowerShell + return [ + this.shell.echo(''), + this.shell.echoColored(` Switched to ${name}...`, 'cyan'), + this.shell.echo(''), + ]; + } + + private generateShBanner(name: string): string[] { + // Rainbow text from center outward + const mid = Math.floor(name.length / 2); + const colorCodes = [ + '\\033[38;2;255;50;50m', // red + '\\033[38;2;255;150;0m', // orange + '\\033[38;2;255;255;0m', // yellow + '\\033[38;2;50;255;100m', // green + '\\033[38;2;0;255;255m', // cyan + '\\033[38;2;80;150;255m', // blue + '\\033[38;2;180;100;255m', // purple + '\\033[38;2;255;100;255m', // magenta + ]; + const reset = '\\033[0m'; + const bold = '\\033[1m'; + const dim = '\\033[2m'; + + const coloredName = + name + .split('') + .map((char, i) => { + const dist = Math.abs(i - mid); + const color = colorCodes[dist % colorCodes.length]; + return `${color}${char}`; + }) + .join('') + reset; + + return [ + `echo -e ""`, + `echo -e " ${dim}Switched to${reset} ${bold}${coloredName}${reset}${dim}...${reset}"`, + `echo -e ""`, + ]; + } + + private generateTable(ctx: EmitContext): string[] { + const { project, fullPath, noEnv } = ctx; + + interface TableRow { + label: string; + value: string; + colorKey: string; + } + + const rows: TableRow[] = [{ label: 'path', value: fullPath, colorKey: 'path' }]; + + if (!noEnv) { + if (project.kubectx) { + rows.push({ label: 'k8s', value: project.kubectx, colorKey: 'k8s' }); + } + if (project.gcloud) { + rows.push({ label: 'gcp', value: project.gcloud, colorKey: 'gcp' }); + } + if (project.aws) { + rows.push({ label: 'aws', value: project.aws, colorKey: 'aws' }); + } + if (project.azure) { + rows.push({ label: 'azure', value: project.azure, colorKey: 'azure' }); + } + if (project.venv) { + rows.push({ label: 'venv', value: project.venv, colorKey: 'venv' }); + } + if (project.env) { + for (const [key, value] of Object.entries(project.env)) { + rows.push({ label: `$${key}`, value, colorKey: key }); + } + } + } + + if (this.shellType === 'sh') { + return this.generateShTable(rows); + } + return this.generateSimpleTable(rows); + } + + private hexToAnsi(hex: string): string { + // Convert #RRGGBB to ANSI escape code + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + return `\\033[38;2;${r};${g};${b}m`; + } + + private generateShTable(rows: { label: string; value: string; colorKey: string }[]): string[] { + const lines: string[] = []; + const reset = '\\033[0m'; + const dim = '\\033[2m'; + const white = '\\033[97m'; + + const maxLabel = Math.max(...rows.map((r) => r.label.length), 6); + const maxValue = Math.max(...rows.map((r) => r.value.length), 10); + const boxWidth = maxLabel + maxValue + 3; + + const topBorder = '┌' + '─'.repeat(boxWidth) + '┐'; + const bottomBorder = '└' + '─'.repeat(boxWidth) + '┘'; + + lines.push(`echo -e " ${white}${topBorder}${reset}"`); + + for (const row of rows) { + const hexColor = getServiceColor(row.colorKey); + const color = this.hexToAnsi(hexColor); + const label = row.label.padEnd(maxLabel); + const value = row.value.padEnd(maxValue); + lines.push( + `echo -e " ${white}│${reset} ${color}${label}${reset} ${dim}${value}${reset} ${white}│${reset}"` + ); + } + + lines.push(`echo -e " ${white}${bottomBorder}${reset}"`); + lines.push(`echo -e ""`); + + return lines; + } + + private generateSimpleTable( + rows: { label: string; value: string; colorKey: string }[] + ): string[] { + const lines: string[] = []; + const maxLabel = Math.max(...rows.map((r) => r.label.length), 8); + + for (const row of rows) { + const label = row.label.padEnd(maxLabel); + if (this.shellType === 'fish') { + lines.push( + `set_color brblack; printf '│ '; set_color cyan; printf '${label}'; set_color normal; printf ' '; set_color green; echo '${row.value}'; set_color normal` + ); + } else { + lines.push( + `Write-Host '│ ' -ForegroundColor DarkGray -NoNewline; Write-Host '${label}' -ForegroundColor Cyan -NoNewline; Write-Host ' ' -NoNewline; Write-Host '${row.value}' -ForegroundColor Green` + ); + } + } + + return lines; + } +} diff --git a/src/emitters/ShellAdapter.ts b/src/emitters/ShellAdapter.ts new file mode 100644 index 0000000..97ea017 --- /dev/null +++ b/src/emitters/ShellAdapter.ts @@ -0,0 +1,187 @@ +import type { ShellType } from '../types.js'; + +export abstract class ShellAdapter { + abstract echo(text: string): string; + abstract echoColored(text: string, color: string): string; + abstract setEnv(key: string, value: string): string; + abstract unsetEnv(key: string): string; + abstract cd(path: string): string; + abstract ifCommandExists(cmd: string, then: string): string; + abstract sourceFile(path: string): string; + abstract fileExists(path: string, then: string): string; + abstract escape(value: string): string; + + static create(shell: ShellType): ShellAdapter { + switch (shell) { + case 'sh': + return new BashAdapter(); + case 'fish': + return new FishAdapter(); + case 'pwsh': + return new PowerShellAdapter(); + default: + throw new Error(`Unknown shell type: ${shell}`); + } + } +} + +export class BashAdapter extends ShellAdapter { + echo(text: string): string { + return `echo -e "${text}"`; + } + + echoColored(text: string, color: string): string { + const colors: Record = { + reset: '\\033[0m', + bold: '\\033[1m', + dim: '\\033[2m', + red: '\\033[38;2;255;50;50m', + orange: '\\033[38;2;255;150;0m', + yellow: '\\033[38;2;255;255;0m', + green: '\\033[38;2;50;255;100m', + cyan: '\\033[38;2;0;255;255m', + blue: '\\033[38;2;80;150;255m', + purple: '\\033[38;2;180;100;255m', + magenta: '\\033[38;2;255;100;255m', + white: '\\033[97m', + }; + const c = colors[color] || ''; + const reset = colors.reset; + return `echo -e "${c}${text}${reset}"`; + } + + setEnv(key: string, value: string): string { + return `export ${key}=${this.escape(value)}`; + } + + unsetEnv(key: string): string { + return `unset ${key}`; + } + + cd(path: string): string { + return `cd ${this.escape(path)}`; + } + + ifCommandExists(cmd: string, then: string): string { + return `command -v ${cmd} >/dev/null 2>&1 && ${then}`; + } + + sourceFile(path: string): string { + return `source ${this.escape(path)}`; + } + + fileExists(path: string, then: string): string { + return `[ -f ${this.escape(path)} ] && ${then}`; + } + + escape(value: string): string { + if (/^[a-zA-Z0-9_/.-]+$/.test(value)) { + return value; + } + return `'${value.replace(/'/g, "'\\''")}'`; + } +} + +export class FishAdapter extends ShellAdapter { + echo(text: string): string { + return `echo '${text}'`; + } + + echoColored(text: string, color: string): string { + const colors: Record = { + red: 'red', + yellow: 'yellow', + green: 'green', + cyan: 'cyan', + blue: 'blue', + magenta: 'magenta', + white: 'white', + dim: 'brblack', + }; + const c = colors[color] || 'normal'; + return `set_color ${c}; echo '${text}'; set_color normal`; + } + + setEnv(key: string, value: string): string { + return `set -gx ${key} ${this.escape(value)}`; + } + + unsetEnv(key: string): string { + return `set -e ${key} 2>/dev/null`; + } + + cd(path: string): string { + return `cd ${this.escape(path)}`; + } + + ifCommandExists(cmd: string, then: string): string { + return `command -q ${cmd}; and ${then}`; + } + + sourceFile(path: string): string { + return `source ${this.escape(path)}`; + } + + fileExists(path: string, then: string): string { + return `test -f ${this.escape(path)}; and ${then}`; + } + + escape(value: string): string { + if (/^[a-zA-Z0-9_/.-]+$/.test(value)) { + return value; + } + return `'${value.replace(/'/g, "\\'")}'`; + } +} + +export class PowerShellAdapter extends ShellAdapter { + echo(text: string): string { + return `Write-Host '${text}'`; + } + + echoColored(text: string, color: string): string { + const colors: Record = { + red: 'Red', + yellow: 'Yellow', + green: 'Green', + cyan: 'Cyan', + blue: 'Blue', + magenta: 'Magenta', + white: 'White', + dim: 'DarkGray', + }; + const c = colors[color] || 'White'; + return `Write-Host '${text}' -ForegroundColor ${c}`; + } + + setEnv(key: string, value: string): string { + return `$env:${key} = ${this.escape(value)}`; + } + + unsetEnv(key: string): string { + return `Remove-Item Env:${key} -ErrorAction SilentlyContinue`; + } + + cd(path: string): string { + return `Set-Location -LiteralPath ${this.escape(path)}`; + } + + ifCommandExists(cmd: string, then: string): string { + return `if (Get-Command ${cmd} -ErrorAction SilentlyContinue) { ${then} }`; + } + + sourceFile(path: string): string { + return `. ${this.escape(path)}`; + } + + fileExists(path: string, then: string): string { + return `if (Test-Path ${this.escape(path)}) { ${then} }`; + } + + escape(value: string): string { + if (/^[a-zA-Z0-9_/.-]+$/.test(value)) { + return `'${value}'`; + } + return `'${value.replace(/'/g, "''")}'`; + } +} diff --git a/src/emitters/index.ts b/src/emitters/index.ts new file mode 100644 index 0000000..5bb18aa --- /dev/null +++ b/src/emitters/index.ts @@ -0,0 +1,23 @@ +import type { Project, ShellType } from '../types.js'; +import { ProjectEmitter } from './ProjectEmitter.js'; + +export interface EmitterContext { + project: Project; + fullPath: string; + noEnv: boolean; +} + +export function emit(shell: ShellType, ctx: EmitterContext): string { + const emitter = new ProjectEmitter(shell); + return emitter.emit(ctx); +} + +export function emitReset(shell: ShellType, projectsDir: string): string { + const emitter = new ProjectEmitter(shell); + return emitter.emitReset(projectsDir); +} + +// Re-export for direct usage +export { ProjectEmitter } from './ProjectEmitter.js'; +export { ShellAdapter } from './ShellAdapter.js'; +export * from './services/index.js'; diff --git a/src/emitters/services/AWSService.ts b/src/emitters/services/AWSService.ts new file mode 100644 index 0000000..9b21d99 --- /dev/null +++ b/src/emitters/services/AWSService.ts @@ -0,0 +1,19 @@ +import { BaseService } from './BaseService.js'; + +export class AWSService extends BaseService { + getLabel(): string { + return 'aws'; + } + + getColor(): string { + return '#FF9900'; // AWS orange + } + + activate(profile: string): string[] { + return [this.shell.setEnv('AWS_PROFILE', profile)]; + } + + deactivate(): string[] { + return [this.shell.unsetEnv('AWS_PROFILE')]; + } +} diff --git a/src/emitters/services/AzureService.ts b/src/emitters/services/AzureService.ts new file mode 100644 index 0000000..7eb5b15 --- /dev/null +++ b/src/emitters/services/AzureService.ts @@ -0,0 +1,21 @@ +import { BaseService } from './BaseService.js'; + +export class AzureService extends BaseService { + getLabel(): string { + return 'azure'; + } + + getColor(): string { + return '#0078D4'; // Azure blue + } + + activate(_account: string): string[] { + // Azure doesn't have a simple activate command like the others + // Just clear and note the account + return [this.shell.ifCommandExists('az', 'az account clear >/dev/null 2>&1')]; + } + + deactivate(): string[] { + return [this.shell.ifCommandExists('az', 'az account clear >/dev/null 2>&1')]; + } +} diff --git a/src/emitters/services/BaseService.ts b/src/emitters/services/BaseService.ts new file mode 100644 index 0000000..2942769 --- /dev/null +++ b/src/emitters/services/BaseService.ts @@ -0,0 +1,10 @@ +import type { ShellAdapter } from '../ShellAdapter.js'; + +export abstract class BaseService { + constructor(protected shell: ShellAdapter) {} + + abstract activate(value: string): string[]; + abstract deactivate(): string[]; + abstract getLabel(): string; + abstract getColor(): string; // Hex color like '#FF5500' +} diff --git a/src/emitters/services/GCloudService.ts b/src/emitters/services/GCloudService.ts new file mode 100644 index 0000000..9c4db71 --- /dev/null +++ b/src/emitters/services/GCloudService.ts @@ -0,0 +1,29 @@ +import { BaseService } from './BaseService.js'; + +export class GCloudService extends BaseService { + getLabel(): string { + return 'gcp'; + } + + getColor(): string { + return '#4285F4'; // Google blue + } + + activate(config: string): string[] { + return [ + this.shell.ifCommandExists( + 'gcloud', + `gcloud config configurations activate ${this.shell.escape(config)} >/dev/null 2>&1` + ), + ]; + } + + deactivate(): string[] { + return [ + this.shell.ifCommandExists( + 'gcloud', + 'gcloud config configurations activate default >/dev/null 2>&1' + ), + ]; + } +} diff --git a/src/emitters/services/KubernetesService.ts b/src/emitters/services/KubernetesService.ts new file mode 100644 index 0000000..0a0e2b3 --- /dev/null +++ b/src/emitters/services/KubernetesService.ts @@ -0,0 +1,21 @@ +import { BaseService } from './BaseService.js'; + +export class KubernetesService extends BaseService { + getLabel(): string { + return 'k8s'; + } + + getColor(): string { + return '#326CE5'; // Kubernetes blue + } + + activate(context: string): string[] { + return [ + this.shell.ifCommandExists('kubectx', `kubectx ${this.shell.escape(context)} >/dev/null`), + ]; + } + + deactivate(): string[] { + return [this.shell.ifCommandExists('kubectx', 'kubectx -u >/dev/null 2>&1')]; + } +} diff --git a/src/emitters/services/PathService.ts b/src/emitters/services/PathService.ts new file mode 100644 index 0000000..19f17c3 --- /dev/null +++ b/src/emitters/services/PathService.ts @@ -0,0 +1,19 @@ +import { BaseService } from './BaseService.js'; + +export class PathService extends BaseService { + getLabel(): string { + return 'path'; + } + + getColor(): string { + return '#AAAAAA'; // Gray for path + } + + activate(path: string): string[] { + return [this.shell.cd(path)]; + } + + deactivate(): string[] { + return []; + } +} diff --git a/src/emitters/services/ServiceRegistry.ts b/src/emitters/services/ServiceRegistry.ts new file mode 100644 index 0000000..5d693db --- /dev/null +++ b/src/emitters/services/ServiceRegistry.ts @@ -0,0 +1,58 @@ +import { ShellAdapter } from '../ShellAdapter.js'; +import { KubernetesService } from './KubernetesService.js'; +import { GCloudService } from './GCloudService.js'; +import { AWSService } from './AWSService.js'; +import { AzureService } from './AzureService.js'; +import { VenvService } from './VenvService.js'; +import { PathService } from './PathService.js'; + +// Service registry - source of truth for all service metadata +export class ServiceRegistry { + private static instance: ServiceRegistry; + private services: Map; + + private constructor() { + // Create a dummy shell adapter just to instantiate services for metadata + const shell = ShellAdapter.create('sh'); + + const k8s = new KubernetesService(shell); + const gcloud = new GCloudService(shell); + const aws = new AWSService(shell); + const azure = new AzureService(shell); + const venv = new VenvService(shell, 'sh'); + const path = new PathService(shell); + + this.services = new Map([ + ['k8s', { label: k8s.getLabel(), color: k8s.getColor() }], + ['gcp', { label: gcloud.getLabel(), color: gcloud.getColor() }], + ['aws', { label: aws.getLabel(), color: aws.getColor() }], + ['azure', { label: azure.getLabel(), color: azure.getColor() }], + ['venv', { label: venv.getLabel(), color: venv.getColor() }], + ['path', { label: path.getLabel(), color: path.getColor() }], + ]); + } + + static getInstance(): ServiceRegistry { + if (!ServiceRegistry.instance) { + ServiceRegistry.instance = new ServiceRegistry(); + } + return ServiceRegistry.instance; + } + + getColor(service: string): string { + return this.services.get(service)?.color || '#AAAAAA'; + } + + getLabel(service: string): string { + return this.services.get(service)?.label || service; + } + + getAllServices(): string[] { + return Array.from(this.services.keys()); + } +} + +// Convenience function for getting colors +export function getServiceColor(service: string): string { + return ServiceRegistry.getInstance().getColor(service); +} diff --git a/src/emitters/services/VenvService.ts b/src/emitters/services/VenvService.ts new file mode 100644 index 0000000..154af04 --- /dev/null +++ b/src/emitters/services/VenvService.ts @@ -0,0 +1,35 @@ +import type { ShellType } from '../../types.js'; +import type { ShellAdapter } from '../ShellAdapter.js'; +import { BaseService } from './BaseService.js'; + +export class VenvService extends BaseService { + constructor( + shell: ShellAdapter, + private shellType: ShellType + ) { + super(shell); + } + + getLabel(): string { + return 'venv'; + } + + getColor(): string { + return '#3776AB'; // Python blue + } + + activate(venvPath: string): string[] { + const activatePath = + this.shellType === 'pwsh' + ? `${venvPath}/Scripts/Activate.ps1` + : this.shellType === 'fish' + ? `${venvPath}/bin/activate.fish` + : `${venvPath}/bin/activate`; + + return [this.shell.fileExists(activatePath, this.shell.sourceFile(activatePath))]; + } + + deactivate(): string[] { + return [this.shell.ifCommandExists('deactivate', 'deactivate 2>/dev/null')]; + } +} diff --git a/src/emitters/services/index.ts b/src/emitters/services/index.ts new file mode 100644 index 0000000..8af70ae --- /dev/null +++ b/src/emitters/services/index.ts @@ -0,0 +1,8 @@ +export { BaseService } from './BaseService.js'; +export { KubernetesService } from './KubernetesService.js'; +export { GCloudService } from './GCloudService.js'; +export { AWSService } from './AWSService.js'; +export { AzureService } from './AzureService.js'; +export { VenvService } from './VenvService.js'; +export { PathService } from './PathService.js'; +export { ServiceRegistry, getServiceColor } from './ServiceRegistry.js'; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..5306a4c --- /dev/null +++ b/src/types.ts @@ -0,0 +1,27 @@ +export interface Project { + name: string; + path: string; + env?: Record; + kubectx?: string; + gcloud?: string; + aws?: string; + azure?: string; + venv?: string; +} + +export interface Icons { + k8s?: string; + gcp?: string; + aws?: string; + azure?: string; + venv?: string; + path?: string; +} + +export interface Config { + projectsDir: string; + projects: Project[]; + icons?: Icons; +} + +export type ShellType = 'sh' | 'fish' | 'pwsh'; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..5cf02ba --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,50 @@ +import { existsSync, statSync } from 'node:fs'; +import Fuse from 'fuse.js'; +import type { Project } from './types.js'; + +export function error(message: string): never { + console.error(`switch-craft: ${message}`); + process.exit(1); +} + +export function validatePathExists(path: string, projectName: string): void { + if (!existsSync(path)) { + error(`Project "${projectName}" path does not exist: ${path}`); + } + + const stat = statSync(path); + if (!stat.isDirectory()) { + error(`Project "${projectName}" path is not a directory: ${path}`); + } +} + +export function findProject(projects: Project[], name: string): Project | null { + // First try exact match + const exact = projects.find((p) => p.name.toLowerCase() === name.toLowerCase()); + if (exact) { + return exact; + } + + // Then try fuzzy search + const fuse = new Fuse(projects, { + keys: ['name'], + threshold: 0.4, + includeScore: true, + }); + + const results = fuse.search(name); + if (results.length > 0 && results[0].score !== undefined && results[0].score <= 0.4) { + return results[0].item; + } + + return null; +} + +export function escapeShellArg(arg: string, shell: 'sh' | 'fish' | 'pwsh'): string { + if (shell === 'pwsh') { + // PowerShell escaping: use single quotes and double any single quotes inside + return `'${arg.replace(/'/g, "''")}'`; + } + // POSIX shell (bash/zsh/fish): use single quotes and escape single quotes + return `'${arg.replace(/'/g, "'\\''")}'`; +} diff --git a/switch-craft.plugin.zsh b/switch-craft.plugin.zsh deleted file mode 100644 index 9dd00cf..0000000 --- a/switch-craft.plugin.zsh +++ /dev/null @@ -1,90 +0,0 @@ -# Switch-Craft Plugin -alias cdx="switch_craft 'Clean Environment'" - -switch_craft() { - # Use existing projects dir from env var if set, otherwise default to ~/Projects - projects_dir=${SWITCH_CRAFT_PROJECTS_DIR:-~/Projects} - - project_name_display=$1 - project_name=$2 - kubectx=${3-'false'} - gcloud=${4-'false'} - enable_venv=${5-'false'} - aws=${6-'false'} - azure=${7-'false'} - - clear; - - # Reset - - while command -v deactivate &> /dev/null - do - echo "Deactivating virtualenv..." - deactivate - sleep 1 - done - - # Go - - if [ $enable_venv != 'false' ] - then - echo "Enabling venv" - cd $projects_dir/$enable_venv; - disable_autoswitch_virtualenv - fi - - if [ $project_name ] - then - echo "Open Project $project_name" - cd $projects_dir/$project_name - - if [ $enable_venv != 'false' ] - then - enable_autoswitch_virtualenv - fi - else - cd $projects_dir - fi - - if [ $kubectx != 'false' ] - then - echo "Setting kubectx to $kubectx" - kubectx $kubectx - else - kubectx -u - fi - - if [ $gcloud != 'false' ] - then - echo "Setting gcloud to $gcloud" - gcloud config configurations activate $gcloud - else - gcloud config configurations activate default - rm ~/.config/gcloud/active_config - fi - - if [ $aws != 'false' ] - then - echo "Setting aws to $aws" - export AWS_PROFILE=$aws - else - unset AWS_PROFILE - fi - - if [ $azure != 'false' ] - then - echo "No mechanism for switching azure yet" - else - az account clear - fi - - echo "\nSwitched to:" - - # If figlet and lolcat are available, use them to display the project name - if command -v figlet &> /dev/null && command -v lolcat &> /dev/null - then - figlet -t -f 'JS Stick Letters' $project_name_display | lolcat - else - echo $project_name_display - fi -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f9b3982 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "jsx": "react-jsx", + "types": ["bun"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}