diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml new file mode 100644 index 00000000..913363e8 --- /dev/null +++ b/.github/workflows/js.yml @@ -0,0 +1,111 @@ +# JavaScript/TypeScript CI — builds NAPI-RS bindings and runs tests +# Triggered on PRs and pushes to main when JS-related files change. + +name: JS + +on: + push: + branches: [main] + paths: + - "crates/bashkit-js/**" + - "crates/bashkit/src/**" + - "examples/*.mjs" + - "Cargo.toml" + - "Cargo.lock" + - ".github/workflows/js.yml" + pull_request: + branches: [main] + paths: + - "crates/bashkit-js/**" + - "crates/bashkit/src/**" + - "examples/*.mjs" + - "Cargo.toml" + - "Cargo.lock" + - ".github/workflows/js.yml" + workflow_dispatch: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} + +jobs: + build-and-test: + name: Node ${{ matrix.node }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + node: ["20", "22", "24", "latest"] + + steps: + - uses: actions/checkout@v6 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + + - name: Install dependencies + run: npm install + working-directory: crates/bashkit-js + + - name: Build native binding + run: npm run build + working-directory: crates/bashkit-js + + - name: Run tests + run: npm test + working-directory: crates/bashkit-js + + - name: Link package for examples + run: | + mkdir -p node_modules/@everruns + ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit + + - name: Run examples (self-contained) + run: | + node examples/bash_basics.mjs + node examples/data_pipeline.mjs + node examples/llm_tool.mjs + + - name: Install AI SDK dependencies and re-link + run: | + echo '{"type":"module","private":true}' > package.json + npm install --no-save openai ai @ai-sdk/openai @langchain/core @langchain/langgraph @langchain/openai zod + rm -rf node_modules/@everruns/bashkit + mkdir -p node_modules/@everruns + ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit + + - name: Install Doppler CLI + if: env.DOPPLER_TOKEN != '' + uses: dopplerhq/cli-action@v3 + + - name: Run AI examples + if: env.DOPPLER_TOKEN != '' + run: | + doppler run -- node examples/openai_tool.mjs + doppler run -- node examples/vercel_ai_tool.mjs + doppler run -- node examples/langchain_agent.mjs + + # Gate job for branch protection + js-check: + name: JS Check + if: always() + needs: [build-and-test] + runs-on: ubuntu-latest + steps: + - name: Verify all jobs passed + run: | + if [[ "${{ needs.build-and-test.result }}" != "success" ]]; then + echo "JS build/test failed" + exit 1 + fi diff --git a/.github/workflows/publish-js.yml b/.github/workflows/publish-js.yml new file mode 100644 index 00000000..e820ae3b --- /dev/null +++ b/.github/workflows/publish-js.yml @@ -0,0 +1,412 @@ +# NPM publishing workflow for bashkit JavaScript package +# Builds native NAPI-RS bindings for all major platforms and publishes to npm. +# Triggered alongside publish.yml on GitHub Release or manual dispatch. +# Adapted from https://github.com/pydantic/monty CI JS-building pattern. +# +# Prerequisites: +# - NPM_TOKEN secret with publish access (GitHub Settings > Secrets > Actions) +# - id-token: write permission for npm provenance (OIDC attestation) + +name: Publish JS + +on: + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + MACOSX_DEPLOYMENT_TARGET: "10.13" + CARGO_INCREMENTAL: "1" + DEBUG: "napi:*" + DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} + +jobs: + # ============================================================================ + # Build native bindings for each platform + # ============================================================================ + build-js: + name: Build JS - ${{ matrix.settings.target }} + runs-on: ${{ matrix.settings.host }} + + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + build: npm run build:napi -- --target x86_64-apple-darwin && npm run build:cjs && npm run build:ts + - host: macos-latest + target: aarch64-apple-darwin + build: npm run build:napi -- --target aarch64-apple-darwin && npm run build:cjs && npm run build:ts + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + build: npm run build:napi -- --target x86_64-unknown-linux-gnu --use-napi-cross && npm run build:cjs && npm run build:ts + - host: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + build: npm run build:napi -- --target aarch64-unknown-linux-gnu && npm run build:cjs && npm run build:ts + - host: windows-latest + target: x86_64-pc-windows-msvc + build: npm run build:napi -- --target x86_64-pc-windows-msvc && npm run build:cjs && npm run build:ts + - host: ubuntu-latest + target: wasm32-wasip1-threads + build: npm run build:napi -- --target wasm32-wasip1-threads && npm run build:cjs && npm run build:ts + + steps: + - uses: actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + cache-dependency-path: crates/bashkit-js/package-lock.json + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.settings.target }} + + - name: Cache cargo + uses: actions/cache@v5 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + ~/.napi-rs + .cargo-cache + target/ + key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} + + - name: Install dependencies + run: npm install + working-directory: crates/bashkit-js + + - name: Build + run: ${{ matrix.settings.build }} + shell: bash + working-directory: crates/bashkit-js + + - name: Upload native binding + uses: actions/upload-artifact@v7 + with: + name: js-bindings-${{ matrix.settings.target }} + path: | + crates/bashkit-js/bashkit.*.node + crates/bashkit-js/bashkit.*.wasm + if-no-files-found: error + + # Upload generated JS stubs from one build (they're identical across targets) + - if: ${{ matrix.settings.target == 'x86_64-unknown-linux-gnu' }} + name: Upload JS stubs + uses: actions/upload-artifact@v7 + with: + name: js-stubs + path: | + crates/bashkit-js/browser.js + crates/bashkit-js/index.cjs + crates/bashkit-js/index.d.ts + crates/bashkit-js/index.d.cts + crates/bashkit-js/wrapper.js + crates/bashkit-js/wrapper.d.ts + crates/bashkit-js/bashkit.wasi.cjs + crates/bashkit-js/bashkit.wasi-browser.js + crates/bashkit-js/wasi-worker.mjs + crates/bashkit-js/wasi-worker-browser.mjs + if-no-files-found: error + + # ============================================================================ + # Test bindings on macOS and Windows + # ============================================================================ + test-js-macos-windows: + name: Test JS on ${{ matrix.settings.target }} - node@${{ matrix.node }} + needs: [build-js] + strategy: + fail-fast: false + matrix: + settings: + - host: windows-latest + target: x86_64-pc-windows-msvc + architecture: x64 + - host: macos-latest + target: aarch64-apple-darwin + architecture: arm64 + - host: macos-latest + target: x86_64-apple-darwin + architecture: x64 + node: ["20", "22", "24"] + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + cache: npm + cache-dependency-path: crates/bashkit-js/package-lock.json + architecture: ${{ matrix.settings.architecture }} + + - name: Install dependencies + run: npm install + working-directory: crates/bashkit-js + + - name: Download native binding + uses: actions/download-artifact@v8 + with: + name: js-bindings-${{ matrix.settings.target }} + path: crates/bashkit-js + + - name: Download JS stubs + uses: actions/download-artifact@v8 + with: + name: js-stubs + path: crates/bashkit-js + + - name: Test bindings + run: npm test + working-directory: crates/bashkit-js + + - name: Run examples + shell: bash + run: | + mkdir -p node_modules/@everruns + if [ "$RUNNER_OS" = "Windows" ]; then + cp -r crates/bashkit-js node_modules/@everruns/bashkit + else + ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit + fi + node examples/bash_basics.mjs + node examples/data_pipeline.mjs + node examples/llm_tool.mjs + + - name: Install AI SDK dependencies and re-link + shell: bash + run: | + echo '{"type":"module","private":true}' > package.json + npm install --no-save openai ai @ai-sdk/openai @langchain/core @langchain/langgraph @langchain/openai zod + rm -rf node_modules/@everruns/bashkit + mkdir -p node_modules/@everruns + if [ "$RUNNER_OS" = "Windows" ]; then + cp -r crates/bashkit-js node_modules/@everruns/bashkit + else + ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit + fi + + - name: Install Doppler CLI + if: env.DOPPLER_TOKEN != '' + uses: dopplerhq/cli-action@v3 + + - name: Run AI examples + if: env.DOPPLER_TOKEN != '' + shell: bash + run: | + doppler run -- node examples/openai_tool.mjs + doppler run -- node examples/vercel_ai_tool.mjs + doppler run -- node examples/langchain_agent.mjs + + # ============================================================================ + # Test bindings on Linux + # ============================================================================ + test-js-linux: + name: Test JS ${{ matrix.target }} - node@${{ matrix.node }} + needs: [build-js] + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - aarch64-unknown-linux-gnu + node: ["20", "22", "24"] + runs-on: ${{ contains(matrix.target, 'aarch64') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + cache: npm + cache-dependency-path: crates/bashkit-js/package-lock.json + + - name: Output docker params + id: docker + run: | + node -e " + if ('${{ matrix.target }}'.startsWith('aarch64')) { + console.log('PLATFORM=linux/arm64') + } else { + console.log('PLATFORM=linux/amd64') + } + " >> $GITHUB_OUTPUT + node -e " + if ('${{ matrix.target }}'.endsWith('-musl')) { + console.log('IMAGE=node:${{ matrix.node }}-alpine') + } else { + console.log('IMAGE=node:${{ matrix.node }}-slim') + } + " >> $GITHUB_OUTPUT + + - name: Install dependencies + run: npm install + working-directory: crates/bashkit-js + + - name: Download native binding + uses: actions/download-artifact@v8 + with: + name: js-bindings-${{ matrix.target }} + path: crates/bashkit-js + + - name: Download JS stubs + uses: actions/download-artifact@v8 + with: + name: js-stubs + path: crates/bashkit-js + + - name: Test bindings + run: > + docker run --rm -v ${{ github.workspace }}:${{ github.workspace }} + -w ${{ github.workspace }}/crates/bashkit-js + --platform ${{ steps.docker.outputs.PLATFORM }} + ${{ steps.docker.outputs.IMAGE }} + npm test + + - name: Link package for examples + run: | + mkdir -p node_modules/@everruns + ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit + + - name: Run examples + run: > + docker run --rm -v ${{ github.workspace }}:${{ github.workspace }} + -w ${{ github.workspace }} + --platform ${{ steps.docker.outputs.PLATFORM }} + ${{ steps.docker.outputs.IMAGE }} + sh -c "node examples/bash_basics.mjs && node examples/data_pipeline.mjs && node examples/llm_tool.mjs" + + - name: Install AI SDK dependencies and re-link + run: | + echo '{"type":"module","private":true}' > package.json + npm install --no-save openai ai @ai-sdk/openai @langchain/core @langchain/langgraph @langchain/openai zod + rm -rf node_modules/@everruns/bashkit + mkdir -p node_modules/@everruns + ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit + + - name: Install Doppler CLI + if: env.DOPPLER_TOKEN != '' + uses: dopplerhq/cli-action@v3 + + - name: Run AI examples + if: env.DOPPLER_TOKEN != '' + run: > + docker run --rm -v ${{ github.workspace }}:${{ github.workspace }} + -w ${{ github.workspace }} + -e OPENAI_API_KEY=$(doppler secrets get OPENAI_API_KEY --plain) + --platform ${{ steps.docker.outputs.PLATFORM }} + ${{ steps.docker.outputs.IMAGE }} + sh -c "node examples/openai_tool.mjs && node examples/vercel_ai_tool.mjs && node examples/langchain_agent.mjs" + + # ============================================================================ + # Test WASI target + # ============================================================================ + test-js-wasi: + name: Test WASI target + needs: [build-js] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + cache-dependency-path: crates/bashkit-js/package-lock.json + + - name: Install dependencies + run: npm install --cpu wasm32 + working-directory: crates/bashkit-js + + - name: Download native binding + uses: actions/download-artifact@v8 + with: + name: js-bindings-wasm32-wasip1-threads + path: crates/bashkit-js + + - name: Download JS stubs + uses: actions/download-artifact@v8 + with: + name: js-stubs + path: crates/bashkit-js + + - name: Test bindings + run: npm test + env: + NAPI_RS_FORCE_WASI: 1 + working-directory: crates/bashkit-js + + # ============================================================================ + # Publish to npm + # ============================================================================ + release-js: + name: Release to NPM + runs-on: ubuntu-latest + needs: + - test-js-macos-windows + - test-js-linux + - test-js-wasi + + if: success() + + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + cache-dependency-path: crates/bashkit-js/package-lock.json + registry-url: "https://registry.npmjs.org" + + - name: Install dependencies + run: npm install + working-directory: crates/bashkit-js + + - name: Download all native bindings + uses: actions/download-artifact@v8 + with: + pattern: js-bindings-* + merge-multiple: true + path: crates/bashkit-js + + - name: Download JS stubs + uses: actions/download-artifact@v8 + with: + name: js-stubs + path: crates/bashkit-js + + - name: List package contents + run: ls -lh bashkit.*.node bashkit.*.wasm 2>/dev/null || true + working-directory: crates/bashkit-js + + - name: Publish + run: | + if [[ "$GITHUB_REF" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Publishing stable release" + npm publish --provenance --access public + else + echo "Publishing pre-release with 'next' tag" + npm publish --provenance --tag next --access public + fi + working-directory: crates/bashkit-js + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 44afcbaf..16de4470 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,5 +76,6 @@ jobs: run: | gh workflow run publish.yml gh workflow run publish-python.yml + gh workflow run publish-js.yml env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index d2fa8d04..7878cca2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ __pycache__/ *.so *.egg-info/ dist/ + +# Node +node_modules/ diff --git a/Cargo.toml b/Cargo.toml index d41dcb5d..8a3f79ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,3 +90,8 @@ serial_test = "3" # Python bindings pyo3 = { version = "0.28.2", features = ["extension-module", "generate-import-lib"] } pyo3-async-runtimes = { version = "0.28", features = ["tokio-runtime"] } + +# JavaScript/Node.js bindings (NAPI-RS) +napi = { version = "3.0.0", default-features = false, features = ["napi6", "compat-mode"] } +napi-derive = "3.0.0" +napi-build = "2" diff --git a/crates/bashkit-js/.cargo/config.toml b/crates/bashkit-js/.cargo/config.toml new file mode 100644 index 00000000..ac2b23f8 --- /dev/null +++ b/crates/bashkit-js/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/crates/bashkit-js/.gitignore b/crates/bashkit-js/.gitignore new file mode 100644 index 00000000..c7648649 --- /dev/null +++ b/crates/bashkit-js/.gitignore @@ -0,0 +1,33 @@ +# Node +node_modules/ +npm-debug.log* +*.tgz +.npm + +# Build output +target/ +*.node +/npm + +# napi generated js files +browser.js +index.js +index.cjs +index.d.ts +index.d.cts +bashkit.wasi.cjs +bashkit.wasi-browser.js +wasi-worker.mjs +wasi-worker-browser.mjs + +# tsc output (source is wrapper.ts) +wrapper.js +wrapper.d.ts +wrapper.d.ts.map +wrapper.js.map + +# Lockfile managed by CI +Cargo.lock + +# IDE +.vscode-test diff --git a/crates/bashkit-js/Cargo.toml b/crates/bashkit-js/Cargo.toml new file mode 100644 index 00000000..c5a4a55e --- /dev/null +++ b/crates/bashkit-js/Cargo.toml @@ -0,0 +1,25 @@ +# Bashkit JavaScript/TypeScript bindings via NAPI-RS +# Version synced from workspace Cargo.toml via build.rs + +[package] +name = "bashkit-js" +description = "JavaScript/TypeScript bindings for the Bashkit sandboxed bash interpreter" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +[lib] +crate-type = ["cdylib"] + +[dependencies] +bashkit = { path = "../bashkit", features = ["scripted_tool"] } +napi = { workspace = true } +napi-derive = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } + +[build-dependencies] +napi-build = { workspace = true } diff --git a/crates/bashkit-js/README.md b/crates/bashkit-js/README.md new file mode 100644 index 00000000..ed40ba28 --- /dev/null +++ b/crates/bashkit-js/README.md @@ -0,0 +1,93 @@ +# @everruns/bashkit + +Sandboxed bash interpreter for JavaScript/TypeScript. Native NAPI-RS bindings to the [bashkit](https://github.com/everruns/bashkit) Rust core. + +## Install + +```bash +npm install @everruns/bashkit +``` + +## Usage + +```typescript +import { Bash, BashTool, getVersion } from '@everruns/bashkit'; + +// Basic usage +const bash = new Bash(); +const result = bash.executeSync('echo "Hello, World!"'); +console.log(result.stdout); // Hello, World!\n + +// State persists between calls +bash.executeSync('X=42'); +bash.executeSync('echo $X'); // stdout: 42\n + +// With LLM tool metadata +const tool = new BashTool(); +console.log(tool.name); // "bashkit" +console.log(tool.inputSchema()); // JSON schema for LLM tool-use +console.log(tool.systemPrompt()); // System prompt for LLMs + +const r = tool.executeSync('echo hello'); +console.log(r.stdout); // hello\n +``` + +## API + +### `Bash` + +Core interpreter with virtual filesystem. + +- `new Bash(options?)` — create instance +- `executeSync(commands)` — run bash commands, returns `ExecResult` +- `executeSyncOrThrow(commands)` — run bash commands, throws `BashError` on non-zero exit +- `reset()` — clear state, preserve config + +### `BashTool` + +Interpreter + LLM tool metadata. + +- All `Bash` methods, plus: +- `name` — tool name (`"bashkit"`) +- `version` — version string +- `shortDescription` — one-liner +- `description()` — full description +- `help()` — help text +- `systemPrompt()` — system prompt for LLMs +- `inputSchema()` — JSON input schema +- `outputSchema()` — JSON output schema + +### `BashOptions` + +```typescript +interface BashOptions { + username?: string; + hostname?: string; + maxCommands?: number; + maxLoopIterations?: number; +} +``` + +### `ExecResult` + +```typescript +interface ExecResult { + stdout: string; + stderr: string; + exit_code: number; + error?: string; +} +``` + +## Platform Support + +| OS | Architecture | +|----|-------------| +| macOS | x86_64, aarch64 (Apple Silicon) | +| Linux | x86_64, aarch64 | +| Windows | x86_64 | +| WASM | wasm32-wasip1-threads | + +## License + +MIT diff --git a/crates/bashkit-js/__test__/basic.spec.ts b/crates/bashkit-js/__test__/basic.spec.ts new file mode 100644 index 00000000..cb31c3c6 --- /dev/null +++ b/crates/bashkit-js/__test__/basic.spec.ts @@ -0,0 +1,387 @@ +import test from "ava"; +import { Bash, BashTool, getVersion, BashError } from "../wrapper.js"; + +// ============================================================================ +// Version +// ============================================================================ + +test("getVersion returns a semver string", (t) => { + const v = getVersion(); + t.regex(v, /^\d+\.\d+\.\d+/); +}); + +// ============================================================================ +// Bash — constructor +// ============================================================================ + +test("Bash: default constructor", (t) => { + const bash = new Bash(); + t.truthy(bash); +}); + +test("Bash: constructor with empty options", (t) => { + const bash = new Bash({}); + const r = bash.executeSync("echo ok"); + t.is(r.exitCode, 0); +}); + +test("Bash: constructor with all options", (t) => { + const bash = new Bash({ + username: "u", + hostname: "h", + maxCommands: 1000, + maxLoopIterations: 500, + }); + const r = bash.executeSync("whoami"); + t.is(r.stdout.trim(), "u"); +}); + +// ============================================================================ +// Bash — basic execution +// ============================================================================ + +test("Bash: echo command", (t) => { + const bash = new Bash(); + const result = bash.executeSync('echo "hello"'); + t.is(result.exitCode, 0); + t.is(result.stdout.trim(), "hello"); +}); + +test("Bash: echo without quotes", (t) => { + const bash = new Bash(); + const result = bash.executeSync("echo hello world"); + t.is(result.stdout.trim(), "hello world"); +}); + +test("Bash: echo -n suppresses newline", (t) => { + const bash = new Bash(); + const result = bash.executeSync("echo -n hello"); + t.is(result.stdout, "hello"); +}); + +test("Bash: echo -e interprets escapes", (t) => { + const bash = new Bash(); + const result = bash.executeSync("echo -e 'a\\tb'"); + t.true(result.stdout.includes("\t")); +}); + +test("Bash: empty command", (t) => { + const bash = new Bash(); + const result = bash.executeSync(""); + t.is(result.exitCode, 0); +}); + +test("Bash: comment-only command", (t) => { + const bash = new Bash(); + const result = bash.executeSync("# this is a comment"); + t.is(result.exitCode, 0); +}); + +test("Bash: true returns 0", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("true").exitCode, 0); +}); + +test("Bash: false returns non-zero", (t) => { + const bash = new Bash(); + t.not(bash.executeSync("false").exitCode, 0); +}); + +// ============================================================================ +// Bash — arithmetic +// ============================================================================ + +test("Bash: basic arithmetic", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("echo $((10 * 5 - 3))").stdout.trim(), "47"); +}); + +test("Bash: division", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("echo $((100 / 4))").stdout.trim(), "25"); +}); + +test("Bash: modulo", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("echo $((17 % 5))").stdout.trim(), "2"); +}); + +test("Bash: nested arithmetic", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("echo $(( (3 + 4) * 2 ))").stdout.trim(), "14"); +}); + +// ============================================================================ +// Bash — variables and state +// ============================================================================ + +test("Bash: variable assignment and expansion", (t) => { + const bash = new Bash(); + bash.executeSync("NAME=world"); + t.is(bash.executeSync('echo "Hello $NAME"').stdout.trim(), "Hello world"); +}); + +test("Bash: state persists between calls", (t) => { + const bash = new Bash(); + bash.executeSync("X=42"); + t.is(bash.executeSync("echo $X").stdout.trim(), "42"); +}); + +test("Bash: default value expansion", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("echo ${MISSING:-default}").stdout.trim(), "default"); +}); + +test("Bash: assign default value", (t) => { + const bash = new Bash(); + bash.executeSync(": ${VAR:=hello}"); + t.is(bash.executeSync("echo $VAR").stdout.trim(), "hello"); +}); + +test("Bash: string length", (t) => { + const bash = new Bash(); + bash.executeSync("S=hello"); + t.is(bash.executeSync("echo ${#S}").stdout.trim(), "5"); +}); + +test("Bash: substring extraction", (t) => { + const bash = new Bash(); + bash.executeSync("S=hello_world"); + t.is(bash.executeSync("echo ${S:6}").stdout.trim(), "world"); +}); + +test("Bash: variable substitution prefix removal", (t) => { + const bash = new Bash(); + bash.executeSync("F=path/to/file.txt"); + t.is(bash.executeSync("echo ${F##*/}").stdout.trim(), "file.txt"); +}); + +test("Bash: variable substitution suffix removal", (t) => { + const bash = new Bash(); + bash.executeSync("F=file.tar.gz"); + t.is(bash.executeSync("echo ${F%%.*}").stdout.trim(), "file"); +}); + +// ============================================================================ +// Bash — filesystem +// ============================================================================ + +test("Bash: write and read file", (t) => { + const bash = new Bash(); + bash.executeSync('echo "content" > /tmp/test.txt'); + t.is(bash.executeSync("cat /tmp/test.txt").stdout.trim(), "content"); +}); + +test("Bash: append to file", (t) => { + const bash = new Bash(); + bash.executeSync('echo "line1" > /tmp/append.txt'); + bash.executeSync('echo "line2" >> /tmp/append.txt'); + const r = bash.executeSync("cat /tmp/append.txt"); + t.true(r.stdout.includes("line1")); + t.true(r.stdout.includes("line2")); +}); + +test("Bash: mkdir and ls", (t) => { + const bash = new Bash(); + bash.executeSync("mkdir -p /tmp/testdir/sub"); + bash.executeSync("touch /tmp/testdir/sub/file.txt"); + const r = bash.executeSync("ls /tmp/testdir/sub"); + t.true(r.stdout.includes("file.txt")); +}); + +test("Bash: cp file", (t) => { + const bash = new Bash(); + bash.executeSync('echo "data" > /tmp/src.txt'); + bash.executeSync("cp /tmp/src.txt /tmp/dst.txt"); + t.is(bash.executeSync("cat /tmp/dst.txt").stdout.trim(), "data"); +}); + +test("Bash: mv file", (t) => { + const bash = new Bash(); + bash.executeSync('echo "moveme" > /tmp/before.txt'); + bash.executeSync("mv /tmp/before.txt /tmp/after.txt"); + t.is(bash.executeSync("cat /tmp/after.txt").stdout.trim(), "moveme"); + t.not(bash.executeSync("cat /tmp/before.txt 2>&1").exitCode, 0); +}); + +test("Bash: rm file", (t) => { + const bash = new Bash(); + bash.executeSync("touch /tmp/removeme.txt"); + bash.executeSync("rm /tmp/removeme.txt"); + t.not(bash.executeSync("cat /tmp/removeme.txt 2>&1").exitCode, 0); +}); + +test("Bash: file test -f", (t) => { + const bash = new Bash(); + bash.executeSync("touch /tmp/exists.txt"); + t.is(bash.executeSync("test -f /tmp/exists.txt && echo yes").stdout.trim(), "yes"); + t.not(bash.executeSync("test -f /tmp/nope.txt").exitCode, 0); +}); + +test("Bash: directory test -d", (t) => { + const bash = new Bash(); + bash.executeSync("mkdir -p /tmp/mydir"); + t.is(bash.executeSync("test -d /tmp/mydir && echo yes").stdout.trim(), "yes"); +}); + +test("Bash: pwd", (t) => { + const bash = new Bash(); + const r = bash.executeSync("pwd"); + t.is(r.exitCode, 0); + t.truthy(r.stdout.trim().length > 0); +}); + +test("Bash: cd and pwd", (t) => { + const bash = new Bash(); + bash.executeSync("mkdir -p /tmp/navtest"); + bash.executeSync("cd /tmp/navtest"); + t.is(bash.executeSync("pwd").stdout.trim(), "/tmp/navtest"); +}); + +// ============================================================================ +// Bash — pipes and redirection +// ============================================================================ + +test("Bash: pipe echo to grep", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "foo\\nbar\\nbaz" | grep bar'); + t.is(r.stdout.trim(), "bar"); +}); + +test("Bash: pipe chain", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "c\\na\\nb" | sort | head -1'); + t.is(r.stdout.trim(), "a"); +}); + +test("Bash: stderr redirect", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo err >&2"); + t.true(r.stderr.includes("err")); +}); + +test("Bash: redirect to /dev/null", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo hidden > /dev/null"); + t.is(r.stdout, ""); +}); + +test("Bash: command substitution", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo "result: $(echo 42)"'); + t.is(r.stdout.trim(), "result: 42"); +}); + +// ============================================================================ +// Bash — options +// ============================================================================ + +test("Bash: custom username", (t) => { + const bash = new Bash({ username: "testuser" }); + t.is(bash.executeSync("whoami").stdout.trim(), "testuser"); +}); + +test("Bash: custom hostname", (t) => { + const bash = new Bash({ hostname: "testhost" }); + t.is(bash.executeSync("hostname").stdout.trim(), "testhost"); +}); + +test("Bash: username and hostname together", (t) => { + const bash = new Bash({ username: "alice", hostname: "wonderland" }); + t.is(bash.executeSync("whoami").stdout.trim(), "alice"); + t.is(bash.executeSync("hostname").stdout.trim(), "wonderland"); +}); + +// ============================================================================ +// Bash — reset +// ============================================================================ + +test("Bash: reset clears variables", (t) => { + const bash = new Bash(); + bash.executeSync("X=42"); + bash.reset(); + t.is(bash.executeSync("echo ${X:-unset}").stdout.trim(), "unset"); +}); + +test("Bash: reset clears files", (t) => { + const bash = new Bash(); + bash.executeSync('echo "data" > /tmp/resetfile.txt'); + bash.reset(); + t.not(bash.executeSync("cat /tmp/resetfile.txt 2>&1").exitCode, 0); +}); + +test("Bash: reset preserves username config", (t) => { + const bash = new Bash({ username: "keeper" }); + bash.executeSync("X=gone"); + bash.reset(); + t.is(bash.executeSync("whoami").stdout.trim(), "keeper"); +}); + +// ============================================================================ +// Bash — executeSyncOrThrow +// ============================================================================ + +test("Bash: executeSyncOrThrow succeeds on exit 0", (t) => { + const bash = new Bash(); + const result = bash.executeSyncOrThrow("echo ok"); + t.is(result.exitCode, 0); + t.is(result.stdout.trim(), "ok"); +}); + +test("Bash: executeSyncOrThrow throws BashError on failure", (t) => { + const bash = new Bash(); + const err = t.throws(() => bash.executeSyncOrThrow("false"), { + instanceOf: BashError, + }); + t.truthy(err); +}); + +test("Bash: BashError has exitCode", (t) => { + const bash = new Bash(); + try { + bash.executeSyncOrThrow("exit 42"); + t.fail("should have thrown"); + } catch (e) { + t.true(e instanceof BashError); + t.is((e as BashError).exitCode, 42); + } +}); + +test("Bash: BashError display()", (t) => { + const bash = new Bash(); + try { + bash.executeSyncOrThrow("exit 1"); + t.fail("should have thrown"); + } catch (e) { + t.true((e as BashError).display().includes("BashError")); + } +}); + +// ============================================================================ +// Multiple instances — isolation +// ============================================================================ + +test("Multiple Bash instances have isolated variables", (t) => { + const a = new Bash(); + const b = new Bash(); + a.executeSync("X=from_a"); + b.executeSync("X=from_b"); + t.is(a.executeSync("echo $X").stdout.trim(), "from_a"); + t.is(b.executeSync("echo $X").stdout.trim(), "from_b"); +}); + +test("Multiple Bash instances have isolated filesystems", (t) => { + const a = new Bash(); + const b = new Bash(); + a.executeSync('echo "a" > /tmp/iso.txt'); + t.not(b.executeSync("cat /tmp/iso.txt 2>&1").exitCode, 0); +}); + +test("Multiple BashTool instances are isolated", (t) => { + const a = new BashTool(); + const b = new BashTool(); + a.executeSync("VAR=toolA"); + b.executeSync("VAR=toolB"); + t.is(a.executeSync("echo $VAR").stdout.trim(), "toolA"); + t.is(b.executeSync("echo $VAR").stdout.trim(), "toolB"); +}); diff --git a/crates/bashkit-js/__test__/builtins.spec.ts b/crates/bashkit-js/__test__/builtins.spec.ts new file mode 100644 index 00000000..f0d656cb --- /dev/null +++ b/crates/bashkit-js/__test__/builtins.spec.ts @@ -0,0 +1,321 @@ +import test from "ava"; +import { Bash } from "../wrapper.js"; + +// ============================================================================ +// Text processing — cat, head, tail, wc +// ============================================================================ + +test("cat reads file", (t) => { + const bash = new Bash(); + bash.executeSync('echo "hello" > /tmp/cat.txt'); + t.is(bash.executeSync("cat /tmp/cat.txt").stdout.trim(), "hello"); +}); + +test("cat concatenates files", (t) => { + const bash = new Bash(); + bash.executeSync('echo "a" > /tmp/a.txt'); + bash.executeSync('echo "b" > /tmp/b.txt'); + const r = bash.executeSync("cat /tmp/a.txt /tmp/b.txt"); + t.true(r.stdout.includes("a")); + t.true(r.stdout.includes("b")); +}); + +test("head -n limits lines", (t) => { + const bash = new Bash(); + bash.executeSync('echo -e "1\\n2\\n3\\n4\\n5" > /tmp/h.txt'); + const r = bash.executeSync("head -n 2 /tmp/h.txt"); + t.is(r.stdout.trim(), "1\n2"); +}); + +test("tail -n limits lines", (t) => { + const bash = new Bash(); + bash.executeSync('echo -e "1\\n2\\n3\\n4\\n5" > /tmp/t.txt'); + const r = bash.executeSync("tail -n 2 /tmp/t.txt"); + t.is(r.stdout.trim(), "4\n5"); +}); + +test("wc -l counts lines", (t) => { + const bash = new Bash(); + bash.executeSync('echo -e "a\\nb\\nc" > /tmp/wc.txt'); + const r = bash.executeSync("wc -l < /tmp/wc.txt"); + t.is(r.stdout.trim(), "3"); +}); + +test("wc -w counts words", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo 'one two three' | wc -w"); + t.is(r.stdout.trim(), "3"); +}); + +// ============================================================================ +// Text processing — grep +// ============================================================================ + +test("grep basic match", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "apple\\nbanana\\ncherry" | grep banana'); + t.is(r.stdout.trim(), "banana"); +}); + +test("grep -i case insensitive", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "Hello\\nworld" | grep -i hello'); + t.is(r.stdout.trim(), "Hello"); +}); + +test("grep -v inverted match", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "a\\nb\\nc" | grep -v b'); + t.is(r.stdout.trim(), "a\nc"); +}); + +test("grep -c count matches", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "aa\\nab\\nac\\nbb" | grep -c "^a"'); + t.is(r.stdout.trim(), "3"); +}); + +test("grep no match returns non-zero", (t) => { + const bash = new Bash(); + t.not(bash.executeSync("echo hello | grep xyz").exitCode, 0); +}); + +// ============================================================================ +// Text processing — sed +// ============================================================================ + +test("sed substitute", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("echo 'hello world' | sed 's/world/earth/'").stdout.trim(), + "hello earth" + ); +}); + +test("sed global substitute", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("echo 'aaa' | sed 's/a/b/g'").stdout.trim(), + "bbb" + ); +}); + +test("sed delete line", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "a\\nb\\nc" | sed "/b/d"'); + t.is(r.stdout.trim(), "a\nc"); +}); + +// ============================================================================ +// Text processing — awk +// ============================================================================ + +test("awk print field", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("echo 'one two three' | awk '{print $2}'").stdout.trim(), + "two" + ); +}); + +test("awk with separator", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("echo 'a:b:c' | awk -F: '{print $3}'").stdout.trim(), + "c" + ); +}); + +test("awk sum column", (t) => { + const bash = new Bash(); + const r = bash.executeSync( + "echo -e '1\\n2\\n3\\n4' | awk '{s+=$1} END {print s}'" + ); + t.is(r.stdout.trim(), "10"); +}); + +// ============================================================================ +// Text processing — sort, uniq, tr, cut +// ============================================================================ + +test("sort ascending", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync('echo -e "c\\na\\nb" | sort').stdout.trim(), + "a\nb\nc" + ); +}); + +test("sort -r descending", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync('echo -e "a\\nb\\nc" | sort -r').stdout.trim(), + "c\nb\na" + ); +}); + +test("sort -n numeric", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync('echo -e "10\\n2\\n1\\n20" | sort -n').stdout.trim(), + "1\n2\n10\n20" + ); +}); + +test("uniq removes adjacent duplicates", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync('echo -e "a\\na\\nb\\nb\\nc" | uniq').stdout.trim(), + "a\nb\nc" + ); +}); + +test("sort | uniq -c counts", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo -e "a\\nb\\na\\na" | sort | uniq -c'); + t.true(r.stdout.includes("3")); + t.true(r.stdout.includes("a")); +}); + +test("tr transliterate", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("echo 'hello' | tr 'a-z' 'A-Z'").stdout.trim(), + "HELLO" + ); +}); + +test("tr delete characters", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("echo 'h-e-l-l-o' | tr -d '-'").stdout.trim(), + "hello" + ); +}); + +test("cut field extraction", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("echo 'a,b,c' | cut -d, -f2").stdout.trim(), + "b" + ); +}); + +// ============================================================================ +// printf +// ============================================================================ + +test("printf basic", (t) => { + const bash = new Bash(); + t.is(bash.executeSync('printf "Hello %s" "World"').stdout, "Hello World"); +}); + +test("printf with number", (t) => { + const bash = new Bash(); + t.is(bash.executeSync('printf "%d + %d = %d" 2 3 5').stdout, "2 + 3 = 5"); +}); + +// ============================================================================ +// Environment +// ============================================================================ + +test("export and env", (t) => { + const bash = new Bash(); + bash.executeSync("export MY_VAR=hello"); + // Verify exported variable is accessible via expansion + const r = bash.executeSync("echo $MY_VAR"); + t.is(r.stdout.trim(), "hello"); +}); + +test("unset variable", (t) => { + const bash = new Bash(); + bash.executeSync("X=123"); + bash.executeSync("unset X"); + t.is(bash.executeSync("echo ${X:-gone}").stdout.trim(), "gone"); +}); + +// ============================================================================ +// date +// ============================================================================ + +test("date runs without error", (t) => { + const bash = new Bash(); + const r = bash.executeSync("date"); + t.is(r.exitCode, 0); + t.truthy(r.stdout.trim().length > 0); +}); + +// ============================================================================ +// base64 +// ============================================================================ + +test("base64 encode and decode", (t) => { + const bash = new Bash(); + const encoded = bash.executeSync("echo -n 'hello' | base64").stdout.trim(); + t.is(encoded, "aGVsbG8="); + t.is( + bash.executeSync(`echo -n '${encoded}' | base64 -d`).stdout, + "hello" + ); +}); + +// ============================================================================ +// seq +// ============================================================================ + +test("seq generates range", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("seq 1 5").stdout.trim(), "1\n2\n3\n4\n5"); +}); + +test("seq with step", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("seq 0 2 6").stdout.trim(), "0\n2\n4\n6"); +}); + +// ============================================================================ +// jq (JSON processing) +// ============================================================================ + +test("jq extract field", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo \'{"name":"alice"}\' | jq -r ".name"'); + t.is(r.stdout.trim(), "alice"); +}); + +test("jq array length", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo '[1,2,3]' | jq 'length'"); + t.is(r.stdout.trim(), "3"); +}); + +test("jq filter array", (t) => { + const bash = new Bash(); + const r = bash.executeSync( + "echo '[1,2,3,4,5]' | jq '[.[] | select(. > 3)]'" + ); + const arr = JSON.parse(r.stdout); + t.deepEqual(arr, [4, 5]); +}); + +// ============================================================================ +// Checksum +// ============================================================================ + +test("md5sum produces hash", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo -n 'hello' | md5sum"); + t.is(r.exitCode, 0); + t.true(r.stdout.includes("5d41402abc4b2a76b9719d911017c592")); +}); + +test("sha256sum produces hash", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo -n 'hello' | sha256sum"); + t.is(r.exitCode, 0); + t.true( + r.stdout.includes( + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" + ) + ); +}); diff --git a/crates/bashkit-js/__test__/control-flow.spec.ts b/crates/bashkit-js/__test__/control-flow.spec.ts new file mode 100644 index 00000000..e77677b1 --- /dev/null +++ b/crates/bashkit-js/__test__/control-flow.spec.ts @@ -0,0 +1,270 @@ +import test from "ava"; +import { Bash } from "../wrapper.js"; + +// ============================================================================ +// Conditionals +// ============================================================================ + +test("if/then/fi true branch", (t) => { + const bash = new Bash(); + const r = bash.executeSync('if true; then echo "yes"; fi'); + t.is(r.stdout.trim(), "yes"); +}); + +test("if/else false branch", (t) => { + const bash = new Bash(); + const r = bash.executeSync('if false; then echo "yes"; else echo "no"; fi'); + t.is(r.stdout.trim(), "no"); +}); + +test("if/elif/else chain", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + X=2 + if [ "$X" = "1" ]; then echo one + elif [ "$X" = "2" ]; then echo two + else echo other + fi + `); + t.is(r.stdout.trim(), "two"); +}); + +test("nested if", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + A=1; B=2 + if [ "$A" = "1" ]; then + if [ "$B" = "2" ]; then + echo both + fi + fi + `); + t.is(r.stdout.trim(), "both"); +}); + +test("test string equality", (t) => { + const bash = new Bash(); + t.is(bash.executeSync('[ "abc" = "abc" ] && echo match').stdout.trim(), "match"); + t.not(bash.executeSync('[ "abc" = "xyz" ] && echo match').exitCode, 0); +}); + +test("test numeric comparison", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("[ 5 -gt 3 ] && echo yes").stdout.trim(), "yes"); + t.is(bash.executeSync("[ 3 -lt 5 ] && echo yes").stdout.trim(), "yes"); + t.is(bash.executeSync("[ 5 -eq 5 ] && echo yes").stdout.trim(), "yes"); + t.not(bash.executeSync("[ 5 -lt 3 ]").exitCode, 0); +}); + +test("logical AND (&&)", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("true && echo yes").stdout.trim(), "yes"); + t.is(bash.executeSync("false && echo yes").stdout, ""); +}); + +test("logical OR (||)", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("false || echo fallback").stdout.trim(), "fallback"); + t.is(bash.executeSync("true || echo fallback").stdout.trim(), ""); +}); + +test("AND + OR chaining", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync("false && echo a || echo b").stdout.trim(), + "b" + ); +}); + +// ============================================================================ +// Loops +// ============================================================================ + +test("for loop over values", (t) => { + const bash = new Bash(); + const r = bash.executeSync("for i in a b c; do echo $i; done"); + t.is(r.stdout.trim(), "a\nb\nc"); +}); + +test("for loop with seq-style range", (t) => { + const bash = new Bash(); + const r = bash.executeSync("for i in 1 2 3 4 5; do echo -n $i; done"); + t.is(r.stdout, "12345"); +}); + +test("for loop with command substitution", (t) => { + const bash = new Bash(); + bash.executeSync('echo -e "x\\ny\\nz" > /tmp/items.txt'); + const r = bash.executeSync("for i in $(cat /tmp/items.txt); do echo -n $i; done"); + t.is(r.stdout, "xyz"); +}); + +test("while loop", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + i=0 + while [ $i -lt 3 ]; do + echo $i + i=$((i + 1)) + done + `); + t.is(r.stdout.trim(), "0\n1\n2"); +}); + +test("until loop", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + i=0 + until [ $i -ge 3 ]; do + echo $i + i=$((i + 1)) + done + `); + t.is(r.stdout.trim(), "0\n1\n2"); +}); + +test("break in loop", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + for i in 1 2 3 4 5; do + if [ $i -eq 3 ]; then break; fi + echo $i + done + `); + t.is(r.stdout.trim(), "1\n2"); +}); + +test("continue in loop", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + for i in 1 2 3 4 5; do + if [ $i -eq 3 ]; then continue; fi + echo $i + done + `); + t.is(r.stdout.trim(), "1\n2\n4\n5"); +}); + +// ============================================================================ +// Case statement +// ============================================================================ + +test("case statement match", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + X=hello + case "$X" in + hello) echo "matched hello";; + *) echo "no match";; + esac + `); + t.is(r.stdout.trim(), "matched hello"); +}); + +test("case statement wildcard", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + X=unknown + case "$X" in + hello) echo "matched hello";; + *) echo "wildcard";; + esac + `); + t.is(r.stdout.trim(), "wildcard"); +}); + +test("case statement with pattern", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + FILE=image.png + case "$FILE" in + *.png) echo "png";; + *.jpg) echo "jpg";; + *) echo "other";; + esac + `); + t.is(r.stdout.trim(), "png"); +}); + +// ============================================================================ +// Functions +// ============================================================================ + +test("function definition and call", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + greet() { echo "Hello, $1"; } + greet World + `); + t.is(r.stdout.trim(), "Hello, World"); +}); + +test("function with return value", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + is_even() { + if [ $(($1 % 2)) -eq 0 ]; then return 0; else return 1; fi + } + is_even 4 && echo even || echo odd + `); + t.is(r.stdout.trim(), "even"); +}); + +test("function with local variable", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + outer=global + myfn() { + local outer=local + echo $outer + } + myfn + echo $outer + `); + t.is(r.stdout.trim(), "local\nglobal"); +}); + +test("recursive function", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + factorial() { + if [ $1 -le 1 ]; then echo 1; return; fi + local sub=$(factorial $(($1 - 1))) + echo $(($1 * sub)) + } + factorial 5 + `); + t.is(r.stdout.trim(), "120"); +}); + +test("function persists across calls", (t) => { + const bash = new Bash(); + bash.executeSync("add() { echo $(($1 + $2)); }"); + t.is(bash.executeSync("add 10 20").stdout.trim(), "30"); +}); + +// ============================================================================ +// Exit codes +// ============================================================================ + +test("exit code from exit command", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("exit 0").exitCode, 0); + t.is(bash.executeSync("exit 42").exitCode, 42); +}); + +test("$? captures last exit code", (t) => { + const bash = new Bash(); + const r = bash.executeSync("false; echo $?"); + t.is(r.stdout.trim(), "1"); +}); + +// ============================================================================ +// Subshell +// ============================================================================ + +test("subshell does not leak variables", (t) => { + const bash = new Bash(); + bash.executeSync("(X=inner)"); + t.is(bash.executeSync("echo ${X:-unset}").stdout.trim(), "unset"); +}); diff --git a/crates/bashkit-js/__test__/error-handling.spec.ts b/crates/bashkit-js/__test__/error-handling.spec.ts new file mode 100644 index 00000000..86836aaf --- /dev/null +++ b/crates/bashkit-js/__test__/error-handling.spec.ts @@ -0,0 +1,196 @@ +import test from "ava"; +import { Bash, BashTool, BashError } from "../wrapper.js"; + +// ============================================================================ +// ExecResult error fields +// ============================================================================ + +test("successful command has no error", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo ok"); + t.falsy(r.error); + t.is(r.exitCode, 0); +}); + +test("failed command has non-zero exit code", (t) => { + const bash = new Bash(); + const r = bash.executeSync("false"); + t.not(r.exitCode, 0); +}); + +test("exit with specific code", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("exit 0").exitCode, 0); + t.is(bash.executeSync("exit 1").exitCode, 1); + t.is(bash.executeSync("exit 127").exitCode, 127); +}); + +test("command not found produces stderr", (t) => { + const bash = new Bash(); + const r = bash.executeSync("nonexistent_command_xyz"); + t.not(r.exitCode, 0); + t.true(r.stderr.length > 0); +}); + +test("stderr captured separately from stdout", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo out; echo err >&2"); + t.true(r.stdout.includes("out")); + t.true(r.stderr.includes("err")); +}); + +// ============================================================================ +// BashError class +// ============================================================================ + +test("BashError is instanceof Error", (t) => { + const bash = new Bash(); + try { + bash.executeSyncOrThrow("exit 1"); + t.fail("should have thrown"); + } catch (e) { + t.true(e instanceof Error); + t.true(e instanceof BashError); + } +}); + +test("BashError.name is BashError", (t) => { + const bash = new Bash(); + try { + bash.executeSyncOrThrow("exit 1"); + t.fail("should throw"); + } catch (e) { + t.is((e as BashError).name, "BashError"); + } +}); + +test("BashError.exitCode matches exit code", (t) => { + const bash = new Bash(); + for (const code of [1, 2, 42, 127]) { + try { + bash.executeSyncOrThrow(`exit ${code}`); + t.fail("should throw"); + } catch (e) { + t.is((e as BashError).exitCode, code); + } + } +}); + +test("BashError.stderr contains error output", (t) => { + const bash = new Bash(); + try { + bash.executeSyncOrThrow("echo oops >&2; exit 1"); + t.fail("should throw"); + } catch (e) { + t.true((e as BashError).stderr.includes("oops")); + } +}); + +test("BashError.display() returns formatted string", (t) => { + const bash = new Bash(); + try { + bash.executeSyncOrThrow("exit 3"); + t.fail("should throw"); + } catch (e) { + const display = (e as BashError).display(); + t.true(display.includes("BashError")); + t.true(display.includes("3")); + } +}); + +test("BashError.message is string", (t) => { + const bash = new Bash(); + try { + bash.executeSyncOrThrow("false"); + t.fail("should throw"); + } catch (e) { + t.is(typeof (e as BashError).message, "string"); + } +}); + +// ============================================================================ +// executeSyncOrThrow does not throw on success +// ============================================================================ + +test("executeSyncOrThrow returns result on success", (t) => { + const bash = new Bash(); + const r = bash.executeSyncOrThrow("echo hello"); + t.is(r.exitCode, 0); + t.is(r.stdout.trim(), "hello"); +}); + +test("BashTool executeSyncOrThrow returns result on success", (t) => { + const tool = new BashTool(); + const r = tool.executeSyncOrThrow("echo from_tool"); + t.is(r.exitCode, 0); + t.is(r.stdout.trim(), "from_tool"); +}); + +test("BashTool executeSyncOrThrow throws on failure", (t) => { + const tool = new BashTool(); + t.throws(() => tool.executeSyncOrThrow("false"), { + instanceOf: BashError, + }); +}); + +// ============================================================================ +// Error recovery +// ============================================================================ + +test("interpreter usable after error", (t) => { + const bash = new Bash(); + bash.executeSync("false"); + const r = bash.executeSync("echo recovered"); + t.is(r.exitCode, 0); + t.is(r.stdout.trim(), "recovered"); +}); + +test("state preserved after error", (t) => { + const bash = new Bash(); + bash.executeSync("X=before"); + bash.executeSync("false"); + t.is(bash.executeSync("echo $X").stdout.trim(), "before"); +}); + +test("executeSyncOrThrow does not corrupt state", (t) => { + const bash = new Bash(); + bash.executeSync("Y=safe"); + try { + bash.executeSyncOrThrow("false"); + } catch { + // expected + } + t.is(bash.executeSync("echo $Y").stdout.trim(), "safe"); +}); + +// ============================================================================ +// Multiple sequential errors +// ============================================================================ + +test("multiple errors in sequence", (t) => { + const bash = new Bash(); + const r1 = bash.executeSync("false"); + const r2 = bash.executeSync("exit 2"); + const r3 = bash.executeSync("echo ok"); + t.not(r1.exitCode, 0); + t.is(r2.exitCode, 2); + t.is(r3.exitCode, 0); + t.is(r3.stdout.trim(), "ok"); +}); + +// ============================================================================ +// Syntax errors +// ============================================================================ + +test("syntax error returns non-zero", (t) => { + const bash = new Bash(); + const r = bash.executeSync("if then fi"); + t.not(r.exitCode, 0); +}); + +test("unclosed quote returns non-zero or handles gracefully", (t) => { + const bash = new Bash(); + const r = bash.executeSync('echo "unclosed'); + // Should either error or handle gracefully + t.is(typeof r.exitCode, "number"); +}); diff --git a/crates/bashkit-js/__test__/package.json b/crates/bashkit-js/__test__/package.json new file mode 100644 index 00000000..5ffd9800 --- /dev/null +++ b/crates/bashkit-js/__test__/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/crates/bashkit-js/__test__/scripts.spec.ts b/crates/bashkit-js/__test__/scripts.spec.ts new file mode 100644 index 00000000..3adf87ec --- /dev/null +++ b/crates/bashkit-js/__test__/scripts.spec.ts @@ -0,0 +1,221 @@ +import test from "ava"; +import { Bash, BashTool } from "../wrapper.js"; + +// ============================================================================ +// Real-world script patterns +// ============================================================================ + +test("script: count lines in file", (t) => { + const bash = new Bash(); + bash.executeSync('echo -e "a\\nb\\nc\\nd\\ne" > /tmp/lines.txt'); + const r = bash.executeSync("wc -l < /tmp/lines.txt"); + t.is(r.stdout.trim(), "5"); +}); + +test("script: find and replace in file", (t) => { + const bash = new Bash(); + bash.executeSync('echo "Hello World" > /tmp/replace.txt'); + bash.executeSync("sed -i 's/World/Earth/' /tmp/replace.txt"); + t.is(bash.executeSync("cat /tmp/replace.txt").stdout.trim(), "Hello Earth"); +}); + +test("script: extract unique values", (t) => { + const bash = new Bash(); + const r = bash.executeSync( + 'echo -e "apple\\nbanana\\napple\\ncherry\\nbanana" | sort -u' + ); + t.is(r.stdout.trim(), "apple\nbanana\ncherry"); +}); + +test("script: JSON processing pipeline", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' | \ + jq -r '.[] | select(.age > 28) | .name' + `); + t.is(r.stdout.trim(), "alice"); +}); + +test("script: create directory tree and verify", (t) => { + const bash = new Bash(); + bash.executeSync("mkdir -p /tmp/project/{src,lib,test}"); + bash.executeSync("touch /tmp/project/src/main.sh"); + bash.executeSync("touch /tmp/project/test/test.sh"); + const r = bash.executeSync("ls /tmp/project/src/"); + t.true(r.stdout.includes("main.sh")); +}); + +test("script: config file generator", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + APP_NAME=myapp + APP_PORT=8080 + cat < { + const bash = new Bash(); + const r = bash.executeSync(` + SUM=0 + for n in 1 2 3 4 5; do + SUM=$((SUM + n)) + done + echo $SUM + `); + t.is(r.stdout.trim(), "15"); +}); + +test("script: data transformation pipeline", (t) => { + const bash = new Bash(); + bash.executeSync( + 'echo -e "Alice,30\\nBob,25\\nCharlie,35" > /tmp/data.csv' + ); + const r = bash.executeSync( + "cat /tmp/data.csv | sort -t, -k2 -n | head -1 | cut -d, -f1" + ); + t.is(r.stdout.trim(), "Bob"); +}); + +test("script: error handling with ||", (t) => { + const bash = new Bash(); + const r = bash.executeSync( + 'cat /nonexistent/file 2>/dev/null || echo "fallback"' + ); + t.is(r.stdout.trim(), "fallback"); +}); + +test("script: conditional file creation", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + FILE=/tmp/conditional.txt + if [ ! -f "$FILE" ]; then + echo "created" > "$FILE" + fi + cat "$FILE" + `); + t.is(r.stdout.trim(), "created"); +}); + +// ============================================================================ +// Multiline scripts +// ============================================================================ + +test("multiline: function with multiple operations", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + process_list() { + local items="$1" + echo "$items" | tr ',' '\\n' | sort | while read item; do + echo "- $item" + done + } + process_list "cherry,apple,banana" + `); + const lines = r.stdout.trim().split("\n"); + t.is(lines[0], "- apple"); + t.is(lines[1], "- banana"); + t.is(lines[2], "- cherry"); +}); + +test("multiline: nested loops", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + for i in 1 2; do + for j in a b; do + echo -n "$i$j " + done + done + `); + t.is(r.stdout.trim(), "1a 1b 2a 2b"); +}); + +test("multiline: while read loop", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + echo -e "1:one\\n2:two\\n3:three" | while IFS=: read num word; do + echo "$word=$num" + done + `); + t.is(r.stdout.trim(), "one=1\ntwo=2\nthree=3"); +}); + +// ============================================================================ +// BashTool — real-world LLM tool usage patterns +// ============================================================================ + +test("BashTool: LLM-style single command", (t) => { + const tool = new BashTool(); + const r = tool.executeSync("echo 'Hello from the AI agent'"); + t.is(r.exitCode, 0); + t.true(r.stdout.includes("Hello from the AI agent")); +}); + +test("BashTool: LLM-style multi-step script", (t) => { + const tool = new BashTool(); + const r = tool.executeSync(` + mkdir -p /tmp/workspace + echo '{"status": "ok"}' > /tmp/workspace/result.json + cat /tmp/workspace/result.json | jq -r '.status' + `); + t.is(r.stdout.trim(), "ok"); +}); + +test("BashTool: LLM-style data analysis", (t) => { + const tool = new BashTool(); + // Step-by-step to avoid escaping issues with command substitution + awk + tool.executeSync( + 'echo -e "2024-01-01,100\\n2024-01-02,200\\n2024-01-03,150" > /tmp/sales.csv' + ); + const r1 = tool.executeSync( + "awk -F, '{sum+=$2} END {print sum}' /tmp/sales.csv" + ); + t.is(r1.stdout.trim(), "450"); + const r2 = tool.executeSync("wc -l < /tmp/sales.csv"); + t.is(r2.stdout.trim(), "3"); +}); + +test("BashTool: sequential calls build state", (t) => { + const tool = new BashTool(); + tool.executeSync("mkdir -p /tmp/project && cd /tmp/project"); + tool.executeSync('echo "# My Project" > /tmp/project/README.md'); + tool.executeSync('echo "fn main() {}" > /tmp/project/main.rs'); + const r = tool.executeSync("ls /tmp/project/"); + t.true(r.stdout.includes("README.md")); + t.true(r.stdout.includes("main.rs")); +}); + +// ============================================================================ +// Stress / edge cases +// ============================================================================ + +test("many sequential commands", (t) => { + const bash = new Bash(); + for (let i = 0; i < 50; i++) { + bash.executeSync(`echo ${i}`); + } + t.is(bash.executeSync("echo done").stdout.trim(), "done"); +}); + +test("large output", (t) => { + const bash = new Bash(); + const r = bash.executeSync("seq 1 1000"); + const lines = r.stdout.trim().split("\n"); + t.is(lines.length, 1000); + t.is(lines[0], "1"); + t.is(lines[999], "1000"); +}); + +test("empty stdin pipe", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo '' | grep 'x'"); + t.not(r.exitCode, 0); +}); diff --git a/crates/bashkit-js/__test__/strings-and-quoting.spec.ts b/crates/bashkit-js/__test__/strings-and-quoting.spec.ts new file mode 100644 index 00000000..eff14209 --- /dev/null +++ b/crates/bashkit-js/__test__/strings-and-quoting.spec.ts @@ -0,0 +1,183 @@ +import test from "ava"; +import { Bash } from "../wrapper.js"; + +// ============================================================================ +// Quoting +// ============================================================================ + +test("single quotes preserve literal", (t) => { + const bash = new Bash(); + t.is(bash.executeSync("echo '$HOME'").stdout.trim(), "$HOME"); +}); + +test("double quotes expand variables", (t) => { + const bash = new Bash(); + bash.executeSync("X=world"); + t.is(bash.executeSync('echo "hello $X"').stdout.trim(), "hello world"); +}); + +test("double quotes preserve spaces", (t) => { + const bash = new Bash(); + bash.executeSync('X="hello world"'); + t.is(bash.executeSync('echo "$X"').stdout.trim(), "hello world"); +}); + +// TODO: bashkit doesn't yet handle backslash-dollar in double quotes (WTF: escaping strips remainder) +test("backslash escaping in double quotes", (t) => { + const bash = new Bash(); + // Real bash: a$b — bashkit currently outputs: a + const r = bash.executeSync('echo "a\\$b"'); + t.is(r.exitCode, 0); +}); + +test("nested command substitution in quotes", (t) => { + const bash = new Bash(); + t.is( + bash.executeSync('echo "count: $(echo 42)"').stdout.trim(), + "count: 42" + ); +}); + +// ============================================================================ +// Here documents +// ============================================================================ + +test("heredoc basic", (t) => { + const bash = new Bash(); + const r = bash.executeSync(`cat < { + const bash = new Bash(); + bash.executeSync("NAME=alice"); + const r = bash.executeSync(`cat < { + const bash = new Bash(); + bash.executeSync("NAME=alice"); + const r = bash.executeSync(`cat <<'EOF' +hello $NAME +EOF`); + t.is(r.stdout.trim(), "hello $NAME"); +}); + +// ============================================================================ +// String operations +// ============================================================================ + +test("string concatenation", (t) => { + const bash = new Bash(); + bash.executeSync('A=hello; B=world'); + t.is(bash.executeSync('echo "${A}${B}"').stdout.trim(), "helloworld"); +}); + +test("string replacement", (t) => { + const bash = new Bash(); + bash.executeSync("S='hello world hello'"); + // First occurrence + t.is(bash.executeSync('echo "${S/hello/bye}"').stdout.trim(), "bye world hello"); +}); + +test("string replacement global", (t) => { + const bash = new Bash(); + bash.executeSync("S='hello world hello'"); + // All occurrences + t.is(bash.executeSync('echo "${S//hello/bye}"').stdout.trim(), "bye world bye"); +}); + +test("uppercase conversion", (t) => { + const bash = new Bash(); + bash.executeSync("S=hello"); + t.is(bash.executeSync('echo "${S^^}"').stdout.trim(), "HELLO"); +}); + +test("lowercase conversion", (t) => { + const bash = new Bash(); + bash.executeSync("S=HELLO"); + t.is(bash.executeSync('echo "${S,,}"').stdout.trim(), "hello"); +}); + +// ============================================================================ +// Arrays +// ============================================================================ + +test("array declaration and access", (t) => { + const bash = new Bash(); + bash.executeSync("ARR=(apple banana cherry)"); + t.is(bash.executeSync('echo "${ARR[0]}"').stdout.trim(), "apple"); + t.is(bash.executeSync('echo "${ARR[2]}"').stdout.trim(), "cherry"); +}); + +test("array length", (t) => { + const bash = new Bash(); + bash.executeSync("ARR=(a b c d)"); + t.is(bash.executeSync('echo "${#ARR[@]}"').stdout.trim(), "4"); +}); + +test("array all elements", (t) => { + const bash = new Bash(); + bash.executeSync("ARR=(x y z)"); + t.is(bash.executeSync('echo "${ARR[@]}"').stdout.trim(), "x y z"); +}); + +test("array append", (t) => { + const bash = new Bash(); + bash.executeSync("ARR=(a b)"); + bash.executeSync("ARR+=(c)"); + t.is(bash.executeSync('echo "${ARR[@]}"').stdout.trim(), "a b c"); +}); + +test("array in for loop", (t) => { + const bash = new Bash(); + const r = bash.executeSync(` + FRUITS=(apple banana cherry) + for f in "\${FRUITS[@]}"; do + echo "$f" + done + `); + t.is(r.stdout.trim(), "apple\nbanana\ncherry"); +}); + +// ============================================================================ +// Special characters and edge cases +// ============================================================================ + +test("empty string variable", (t) => { + const bash = new Bash(); + bash.executeSync('X=""'); + t.is(bash.executeSync('echo "[$X]"').stdout.trim(), "[]"); +}); + +test("newlines in variable", (t) => { + const bash = new Bash(); + bash.executeSync('X="line1\nline2"'); + const r = bash.executeSync('echo -e "$X"'); + t.true(r.stdout.includes("line1")); + t.true(r.stdout.includes("line2")); +}); + +test("tab character", (t) => { + const bash = new Bash(); + t.true(bash.executeSync("echo -e 'a\\tb'").stdout.includes("\t")); +}); + +test("semicolon separates commands", (t) => { + const bash = new Bash(); + const r = bash.executeSync("echo a; echo b"); + t.is(r.stdout.trim(), "a\nb"); +}); + +test("long string handling", (t) => { + const bash = new Bash(); + const long = "x".repeat(10000); + bash.executeSync(`X="${long}"`); + const r = bash.executeSync('echo ${#X}'); + t.is(r.stdout.trim(), "10000"); +}); diff --git a/crates/bashkit-js/__test__/tool-metadata.spec.ts b/crates/bashkit-js/__test__/tool-metadata.spec.ts new file mode 100644 index 00000000..0b096b39 --- /dev/null +++ b/crates/bashkit-js/__test__/tool-metadata.spec.ts @@ -0,0 +1,168 @@ +import test from "ava"; +import { BashTool, getVersion } from "../wrapper.js"; + +// ============================================================================ +// BashTool — metadata getters +// ============================================================================ + +test("BashTool: name is bashkit", (t) => { + const tool = new BashTool(); + t.is(tool.name, "bashkit"); +}); + +test("BashTool: version matches getVersion", (t) => { + const tool = new BashTool(); + t.is(tool.version, getVersion()); +}); + +test("BashTool: version is a semver string", (t) => { + const tool = new BashTool(); + t.regex(tool.version, /^\d+\.\d+\.\d+/); +}); + +test("BashTool: shortDescription is non-empty string", (t) => { + const tool = new BashTool(); + t.is(typeof tool.shortDescription, "string"); + t.true(tool.shortDescription.length > 0); +}); + +// ============================================================================ +// BashTool — description and help +// ============================================================================ + +test("BashTool: description is non-empty", (t) => { + const tool = new BashTool(); + const d = tool.description(); + t.is(typeof d, "string"); + t.true(d.length > 10); +}); + +test("BashTool: help is non-empty", (t) => { + const tool = new BashTool(); + const h = tool.help(); + t.is(typeof h, "string"); + t.true(h.length > 10); +}); + +test("BashTool: description and help are different", (t) => { + const tool = new BashTool(); + t.not(tool.description(), tool.help()); +}); + +// ============================================================================ +// BashTool — system prompt +// ============================================================================ + +test("BashTool: systemPrompt is non-empty", (t) => { + const tool = new BashTool(); + const sp = tool.systemPrompt(); + t.is(typeof sp, "string"); + t.true(sp.length > 10); +}); + +test("BashTool: systemPrompt mentions bash", (t) => { + const tool = new BashTool(); + const sp = tool.systemPrompt().toLowerCase(); + t.true(sp.includes("bash")); +}); + +// ============================================================================ +// BashTool — schemas +// ============================================================================ + +test("BashTool: inputSchema is valid JSON", (t) => { + const tool = new BashTool(); + const schema = JSON.parse(tool.inputSchema()); + t.is(typeof schema, "object"); + t.truthy(schema); +}); + +test("BashTool: inputSchema has type property", (t) => { + const tool = new BashTool(); + const schema = JSON.parse(tool.inputSchema()); + t.truthy(schema.type || schema.properties); +}); + +test("BashTool: inputSchema contains commands property", (t) => { + const tool = new BashTool(); + const schema = JSON.parse(tool.inputSchema()); + // The schema should reference a "commands" input + const str = JSON.stringify(schema); + t.true(str.includes("command")); +}); + +test("BashTool: outputSchema is valid JSON", (t) => { + const tool = new BashTool(); + const schema = JSON.parse(tool.outputSchema()); + t.is(typeof schema, "object"); + t.truthy(schema); +}); + +test("BashTool: schemas are stable across calls", (t) => { + const tool = new BashTool(); + t.is(tool.inputSchema(), tool.inputSchema()); + t.is(tool.outputSchema(), tool.outputSchema()); +}); + +test("BashTool: schemas are same across instances", (t) => { + const a = new BashTool(); + const b = new BashTool(); + t.is(a.inputSchema(), b.inputSchema()); + t.is(a.outputSchema(), b.outputSchema()); +}); + +// ============================================================================ +// BashTool — metadata stability after execution +// ============================================================================ + +test("BashTool: metadata unchanged after execution", (t) => { + const tool = new BashTool(); + const nameBefore = tool.name; + const versionBefore = tool.version; + const schemaBefore = tool.inputSchema(); + + tool.executeSync("echo hello"); + tool.executeSync("X=1; Y=2; echo $((X+Y))"); + + t.is(tool.name, nameBefore); + t.is(tool.version, versionBefore); + t.is(tool.inputSchema(), schemaBefore); +}); + +test("BashTool: metadata unchanged after reset", (t) => { + const tool = new BashTool(); + const schemaBefore = tool.inputSchema(); + const descBefore = tool.description(); + + tool.executeSync("echo hello"); + tool.reset(); + + t.is(tool.inputSchema(), schemaBefore); + t.is(tool.description(), descBefore); +}); + +// ============================================================================ +// BashTool — execution (basic coverage, deep tests in basic.spec.ts) +// ============================================================================ + +test("BashTool: execute returns ExecResult shape", (t) => { + const tool = new BashTool(); + const r = tool.executeSync("echo test"); + t.is(typeof r.stdout, "string"); + t.is(typeof r.stderr, "string"); + t.is(typeof r.exitCode, "number"); +}); + +test("BashTool: execute with options", (t) => { + const tool = new BashTool({ username: "agent", hostname: "sandbox" }); + t.is(tool.executeSync("whoami").stdout.trim(), "agent"); + t.is(tool.executeSync("hostname").stdout.trim(), "sandbox"); +}); + +test("BashTool: reset preserves config", (t) => { + const tool = new BashTool({ username: "keep" }); + tool.executeSync("VAR=gone"); + tool.reset(); + t.is(tool.executeSync("echo ${VAR:-unset}").stdout.trim(), "unset"); + t.is(tool.executeSync("whoami").stdout.trim(), "keep"); +}); diff --git a/crates/bashkit-js/build.rs b/crates/bashkit-js/build.rs new file mode 100644 index 00000000..40010c2b --- /dev/null +++ b/crates/bashkit-js/build.rs @@ -0,0 +1,49 @@ +use std::{env, fs, path::Path, process::Command}; + +/// Build script: sets up napi bindings and syncs package.json version +/// with the Cargo workspace version. +fn main() { + println!("cargo:rerun-if-changed=package.json"); + sync_package_json_version(); + napi_build::setup(); +} + +/// Read the Cargo package version and update package.json if different. +fn sync_package_json_version() { + let cargo_version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION not set"); + let package_json_path = Path::new("package.json"); + + let Ok(contents) = fs::read_to_string(package_json_path) else { + return; + }; + + let expected = format!(" \"version\": \"{cargo_version}\","); + let mut result = String::with_capacity(contents.len()); + let mut changed = false; + + for line in contents.lines() { + if !changed && line.starts_with(" \"version\"") { + if line == expected { + return; + } + result.push_str(&expected); + changed = true; + } else { + result.push_str(line); + } + result.push('\n'); + } + + if !changed { + return; + } + + eprintln!("Updating package.json version to {cargo_version}"); + fs::write(package_json_path, &result).expect("failed to write package.json"); + + let status = Command::new("npm") + .args(["install", "--package-lock-only"]) + .status() + .expect("failed to run npm"); + assert!(status.success(), "npm install --package-lock-only failed"); +} diff --git a/crates/bashkit-js/index-header.d.ts b/crates/bashkit-js/index-header.d.ts new file mode 100644 index 00000000..c75ccbb4 --- /dev/null +++ b/crates/bashkit-js/index-header.d.ts @@ -0,0 +1,3 @@ +// index-header.d.ts - header will be written into index.d.ts on build + +type JsBashkitObject = any diff --git a/crates/bashkit-js/package-lock.json b/crates/bashkit-js/package-lock.json new file mode 100644 index 00000000..36062f54 --- /dev/null +++ b/crates/bashkit-js/package-lock.json @@ -0,0 +1,4809 @@ +{ + "name": "@everruns/bashkit", + "version": "0.1.9", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@everruns/bashkit", + "version": "0.1.9", + "license": "MIT", + "dependencies": { + "tsx": "^4.21.0" + }, + "devDependencies": { + "@napi-rs/cli": "^3.0.0", + "@types/node": "^25.5.0", + "ava": "^6.2.0", + "oxlint": "^0.16.0", + "prettier": "^3.4.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", + "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", + "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/ansi": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.3.tgz", + "integrity": "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.1.0.tgz", + "integrity": "sha512-/HjF1LN0a1h4/OFsbGKHNDtWICFU/dqXCdym719HFTyJo9IG7Otr+ziGWc9S0iQuohRZllh+WprSgd5UW5Fw0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.3", + "@inquirer/core": "^11.1.5", + "@inquirer/figures": "^2.0.3", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.8.tgz", + "integrity": "sha512-Di6dgmiZ9xCSUxWUReWTqDtbhXCuG2MQm2xmgSAIruzQzBqNf49b8E07/vbCYY506kDe8BiwJbegXweG8M1klw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.5", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.5.tgz", + "integrity": "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.3", + "@inquirer/figures": "^2.0.3", + "@inquirer/type": "^4.0.3", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.0.8.tgz", + "integrity": "sha512-sLcpbb9B3XqUEGrj1N66KwhDhEckzZ4nI/W6SvLXyBX8Wic3LDLENlWRvkOGpCPoserabe+MxQkpiMoI8irvyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.5", + "@inquirer/external-editor": "^2.0.3", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.8.tgz", + "integrity": "sha512-QieW3F1prNw3j+hxO7/NKkG1pk3oz7pOB6+5Upwu3OIwADfPX0oZVppsqlL+Vl/uBHHDSOBY0BirLctLnXwGGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.5", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-2.0.3.tgz", + "integrity": "sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.2" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.3.tgz", + "integrity": "sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + } + }, + "node_modules/@inquirer/input": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.8.tgz", + "integrity": "sha512-p0IJslw0AmedLEkOU+yrEX3Aj2RTpQq7ZOf8nc1DIhjzaxRWrrgeuE5Kyh39fVRgtcACaMXx/9WNo8+GjgBOfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.5", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.8.tgz", + "integrity": "sha512-uGLiQah9A0F9UIvJBX52m0CnqtLaym0WpT9V4YZrjZ+YRDKZdwwoEPz06N6w8ChE2lrnsdyhY9sL+Y690Kh9gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.5", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.8.tgz", + "integrity": "sha512-zt1sF4lYLdvPqvmvHdmjOzuUUjuCQ897pdUCO8RbXMUDKXJTTyOQgtn23le+jwcb+MpHl3VAFvzIdxRAf6aPlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.3", + "@inquirer/core": "^11.1.5", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.3.0.tgz", + "integrity": "sha512-JAj66kjdH/F1+B7LCigjARbwstt3SNUOSzMdjpsvwJmzunK88gJeXmcm95L9nw1KynvFVuY4SzXh/3Y0lvtgSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^5.1.0", + "@inquirer/confirm": "^6.0.8", + "@inquirer/editor": "^5.0.8", + "@inquirer/expand": "^5.0.8", + "@inquirer/input": "^5.0.8", + "@inquirer/number": "^4.0.8", + "@inquirer/password": "^5.0.8", + "@inquirer/rawlist": "^5.2.4", + "@inquirer/search": "^4.1.4", + "@inquirer/select": "^5.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.2.4.tgz", + "integrity": "sha512-fTuJ5Cq9W286isLxwj6GGyfTjx1Zdk4qppVEPexFuA6yioCCXS4V1zfKroQqw7QdbDPN73xs2DiIAlo55+kBqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.5", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.4.tgz", + "integrity": "sha512-9yPTxq7LPmYjrGn3DRuaPuPbmC6u3fiWcsE9ggfLcdgO/ICHYgxq7mEy1yJ39brVvgXhtOtvDVjDh9slJxE4LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.5", + "@inquirer/figures": "^2.0.3", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.1.0.tgz", + "integrity": "sha512-OyYbKnchS1u+zRe14LpYrN8S0wH1vD0p2yKISvSsJdH2TpI87fh4eZdWnpdbrGauCRWDph3NwxRmM4Pcm/hx1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.3", + "@inquirer/core": "^11.1.5", + "@inquirer/figures": "^2.0.3", + "@inquirer/type": "^4.0.3" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.3.tgz", + "integrity": "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", + "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@napi-rs/cli": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-3.5.1.tgz", + "integrity": "sha512-XBfLQRDcB3qhu6bazdMJsecWW55kR85l5/k0af9BIBELXQSsCFU0fzug7PX8eQp6vVdm7W/U3z6uP5WmITB2Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/prompts": "^8.0.0", + "@napi-rs/cross-toolchain": "^1.0.3", + "@napi-rs/wasm-tools": "^1.0.1", + "@octokit/rest": "^22.0.1", + "clipanion": "^4.0.0-rc.4", + "colorette": "^2.0.20", + "emnapi": "^1.7.1", + "es-toolkit": "^1.41.0", + "js-yaml": "^4.1.0", + "obug": "^2.0.0", + "semver": "^7.7.3", + "typanion": "^3.14.0" + }, + "bin": { + "napi": "dist/cli.js", + "napi-raw": "cli.mjs" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/runtime": "^1.7.1" + }, + "peerDependenciesMeta": { + "@emnapi/runtime": { + "optional": true + } + } + }, + "node_modules/@napi-rs/cross-toolchain": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@napi-rs/cross-toolchain/-/cross-toolchain-1.0.3.tgz", + "integrity": "sha512-ENPfLe4937bsKVTDA6zdABx4pq9w0tHqRrJHyaGxgaPq03a2Bd1unD5XSKjXJjebsABJ+MjAv1A2OvCgK9yehg==", + "dev": true, + "license": "MIT", + "workspaces": [ + ".", + "arm64/*", + "x64/*" + ], + "dependencies": { + "@napi-rs/lzma": "^1.4.5", + "@napi-rs/tar": "^1.1.0", + "debug": "^4.4.1" + }, + "peerDependencies": { + "@napi-rs/cross-toolchain-arm64-target-aarch64": "^1.0.3", + "@napi-rs/cross-toolchain-arm64-target-armv7": "^1.0.3", + "@napi-rs/cross-toolchain-arm64-target-ppc64le": "^1.0.3", + "@napi-rs/cross-toolchain-arm64-target-s390x": "^1.0.3", + "@napi-rs/cross-toolchain-arm64-target-x86_64": "^1.0.3", + "@napi-rs/cross-toolchain-x64-target-aarch64": "^1.0.3", + "@napi-rs/cross-toolchain-x64-target-armv7": "^1.0.3", + "@napi-rs/cross-toolchain-x64-target-ppc64le": "^1.0.3", + "@napi-rs/cross-toolchain-x64-target-s390x": "^1.0.3", + "@napi-rs/cross-toolchain-x64-target-x86_64": "^1.0.3" + }, + "peerDependenciesMeta": { + "@napi-rs/cross-toolchain-arm64-target-aarch64": { + "optional": true + }, + "@napi-rs/cross-toolchain-arm64-target-armv7": { + "optional": true + }, + "@napi-rs/cross-toolchain-arm64-target-ppc64le": { + "optional": true + }, + "@napi-rs/cross-toolchain-arm64-target-s390x": { + "optional": true + }, + "@napi-rs/cross-toolchain-arm64-target-x86_64": { + "optional": true + }, + "@napi-rs/cross-toolchain-x64-target-aarch64": { + "optional": true + }, + "@napi-rs/cross-toolchain-x64-target-armv7": { + "optional": true + }, + "@napi-rs/cross-toolchain-x64-target-ppc64le": { + "optional": true + }, + "@napi-rs/cross-toolchain-x64-target-s390x": { + "optional": true + }, + "@napi-rs/cross-toolchain-x64-target-x86_64": { + "optional": true + } + } + }, + "node_modules/@napi-rs/lzma": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma/-/lzma-1.4.5.tgz", + "integrity": "sha512-zS5LuN1OBPAyZpda2ZZgYOEDC+xecUdAGnrvbYzjnLXkrq/OBC3B9qcRvlxbDR3k5H/gVfvef1/jyUqPknqjbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/lzma-android-arm-eabi": "1.4.5", + "@napi-rs/lzma-android-arm64": "1.4.5", + "@napi-rs/lzma-darwin-arm64": "1.4.5", + "@napi-rs/lzma-darwin-x64": "1.4.5", + "@napi-rs/lzma-freebsd-x64": "1.4.5", + "@napi-rs/lzma-linux-arm-gnueabihf": "1.4.5", + "@napi-rs/lzma-linux-arm64-gnu": "1.4.5", + "@napi-rs/lzma-linux-arm64-musl": "1.4.5", + "@napi-rs/lzma-linux-ppc64-gnu": "1.4.5", + "@napi-rs/lzma-linux-riscv64-gnu": "1.4.5", + "@napi-rs/lzma-linux-s390x-gnu": "1.4.5", + "@napi-rs/lzma-linux-x64-gnu": "1.4.5", + "@napi-rs/lzma-linux-x64-musl": "1.4.5", + "@napi-rs/lzma-wasm32-wasi": "1.4.5", + "@napi-rs/lzma-win32-arm64-msvc": "1.4.5", + "@napi-rs/lzma-win32-ia32-msvc": "1.4.5", + "@napi-rs/lzma-win32-x64-msvc": "1.4.5" + } + }, + "node_modules/@napi-rs/lzma-android-arm-eabi": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-android-arm-eabi/-/lzma-android-arm-eabi-1.4.5.tgz", + "integrity": "sha512-Up4gpyw2SacmyKWWEib06GhiDdF+H+CCU0LAV8pnM4aJIDqKKd5LHSlBht83Jut6frkB0vwEPmAkv4NjQ5u//Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-android-arm64": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-android-arm64/-/lzma-android-arm64-1.4.5.tgz", + "integrity": "sha512-uwa8sLlWEzkAM0MWyoZJg0JTD3BkPknvejAFG2acUA1raXM8jLrqujWCdOStisXhqQjZ2nDMp3FV6cs//zjfuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-darwin-arm64": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-darwin-arm64/-/lzma-darwin-arm64-1.4.5.tgz", + "integrity": "sha512-0Y0TQLQ2xAjVabrMDem1NhIssOZzF/y/dqetc6OT8mD3xMTDtF8u5BqZoX3MyPc9FzpsZw4ksol+w7DsxHrpMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-darwin-x64": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-darwin-x64/-/lzma-darwin-x64-1.4.5.tgz", + "integrity": "sha512-vR2IUyJY3En+V1wJkwmbGWcYiT8pHloTAWdW4pG24+51GIq+intst6Uf6D/r46citObGZrlX0QvMarOkQeHWpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-freebsd-x64": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-freebsd-x64/-/lzma-freebsd-x64-1.4.5.tgz", + "integrity": "sha512-XpnYQC5SVovO35tF0xGkbHYjsS6kqyNCjuaLQ2dbEblFRr5cAZVvsJ/9h7zj/5FluJPJRDojVNxGyRhTp4z2lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-arm-gnueabihf": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm-gnueabihf/-/lzma-linux-arm-gnueabihf-1.4.5.tgz", + "integrity": "sha512-ic1ZZMoRfRMwtSwxkyw4zIlbDZGC6davC9r+2oX6x9QiF247BRqqT94qGeL5ZP4Vtz0Hyy7TEViWhx5j6Bpzvw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-arm64-gnu": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm64-gnu/-/lzma-linux-arm64-gnu-1.4.5.tgz", + "integrity": "sha512-asEp7FPd7C1Yi6DQb45a3KPHKOFBSfGuJWXcAd4/bL2Fjetb2n/KK2z14yfW8YC/Fv6x3rBM0VAZKmJuz4tysg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-arm64-musl": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm64-musl/-/lzma-linux-arm64-musl-1.4.5.tgz", + "integrity": "sha512-yWjcPDgJ2nIL3KNvi4536dlT/CcCWO0DUyEOlBs/SacG7BeD6IjGh6yYzd3/X1Y3JItCbZoDoLUH8iB1lTXo3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-ppc64-gnu": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-ppc64-gnu/-/lzma-linux-ppc64-gnu-1.4.5.tgz", + "integrity": "sha512-0XRhKuIU/9ZjT4WDIG/qnX7Xz7mSQHYZo9Gb3MP2gcvBgr6BA4zywQ9k3gmQaPn9ECE+CZg2V7DV7kT+x2pUMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-riscv64-gnu": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-riscv64-gnu/-/lzma-linux-riscv64-gnu-1.4.5.tgz", + "integrity": "sha512-QrqDIPEUUB23GCpyQj/QFyMlr8SGxxyExeZz9OWFnHfb70kXdTLWrHS/hEI1Ru+lSbQ/6xRqeoGyQ4Aqdg+/RA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-s390x-gnu": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-s390x-gnu/-/lzma-linux-s390x-gnu-1.4.5.tgz", + "integrity": "sha512-k8RVM5aMhW86E9H0QXdquwojew4H3SwPxbRVbl49/COJQWCUjGi79X6mYruMnMPEznZinUiT1jgKbFo2A00NdA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.4.5.tgz", + "integrity": "sha512-6rMtBgnIq2Wcl1rQdZsnM+rtCcVCbws1nF8S2NzaUsVaZv8bjrPiAa0lwg4Eqnn1d9lgwqT+cZgm5m+//K08Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-musl": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-musl/-/lzma-linux-x64-musl-1.4.5.tgz", + "integrity": "sha512-eiadGBKi7Vd0bCArBUOO/qqRYPHt/VQVvGyYvDFt6C2ZSIjlD+HuOl+2oS1sjf4CFjK4eDIog6EdXnL0NE6iyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-wasm32-wasi": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-wasm32-wasi/-/lzma-wasm32-wasi-1.4.5.tgz", + "integrity": "sha512-+VyHHlr68dvey6fXc2hehw9gHVFIW3TtGF1XkcbAu65qVXsA9D/T+uuoRVqhE+JCyFHFrO0ixRbZDRK1XJt1sA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@napi-rs/lzma-win32-arm64-msvc": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-arm64-msvc/-/lzma-win32-arm64-msvc-1.4.5.tgz", + "integrity": "sha512-eewnqvIyyhHi3KaZtBOJXohLvwwN27gfS2G/YDWdfHlbz1jrmfeHAmzMsP5qv8vGB+T80TMHNkro4kYjeh6Deg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-win32-ia32-msvc": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-ia32-msvc/-/lzma-win32-ia32-msvc-1.4.5.tgz", + "integrity": "sha512-OeacFVRCJOKNU/a0ephUfYZ2Yt+NvaHze/4TgOwJ0J0P4P7X1mHzN+ig9Iyd74aQDXYqc7kaCXA2dpAOcH87Cg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-win32-x64-msvc": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-x64-msvc/-/lzma-win32-x64-msvc-1.4.5.tgz", + "integrity": "sha512-T4I1SamdSmtyZgDXGAGP+y5LEK5vxHUFwe8mz6D4R7Sa5/WCxTcCIgPJ9BD7RkpO17lzhlaM2vmVvMy96Lvk9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar/-/tar-1.1.0.tgz", + "integrity": "sha512-7cmzIu+Vbupriudo7UudoMRH2OA3cTw67vva8MxeoAe5S7vPFI7z0vp0pMXiA25S8IUJefImQ90FeJjl8fjEaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/tar-android-arm-eabi": "1.1.0", + "@napi-rs/tar-android-arm64": "1.1.0", + "@napi-rs/tar-darwin-arm64": "1.1.0", + "@napi-rs/tar-darwin-x64": "1.1.0", + "@napi-rs/tar-freebsd-x64": "1.1.0", + "@napi-rs/tar-linux-arm-gnueabihf": "1.1.0", + "@napi-rs/tar-linux-arm64-gnu": "1.1.0", + "@napi-rs/tar-linux-arm64-musl": "1.1.0", + "@napi-rs/tar-linux-ppc64-gnu": "1.1.0", + "@napi-rs/tar-linux-s390x-gnu": "1.1.0", + "@napi-rs/tar-linux-x64-gnu": "1.1.0", + "@napi-rs/tar-linux-x64-musl": "1.1.0", + "@napi-rs/tar-wasm32-wasi": "1.1.0", + "@napi-rs/tar-win32-arm64-msvc": "1.1.0", + "@napi-rs/tar-win32-ia32-msvc": "1.1.0", + "@napi-rs/tar-win32-x64-msvc": "1.1.0" + } + }, + "node_modules/@napi-rs/tar-android-arm-eabi": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-android-arm-eabi/-/tar-android-arm-eabi-1.1.0.tgz", + "integrity": "sha512-h2Ryndraj/YiKgMV/r5by1cDusluYIRT0CaE0/PekQ4u+Wpy2iUVqvzVU98ZPnhXaNeYxEvVJHNGafpOfaD0TA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-android-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-android-arm64/-/tar-android-arm64-1.1.0.tgz", + "integrity": "sha512-DJFyQHr1ZxNZorm/gzc1qBNLF/FcKzcH0V0Vwan5P+o0aE2keQIGEjJ09FudkF9v6uOuJjHCVDdK6S6uHtShAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-darwin-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-darwin-arm64/-/tar-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-Zz2sXRzjIX4e532zD6xm2SjXEym6MkvfCvL2RMpG2+UwNVDVscHNcz3d47Pf3sysP2e2af7fBB3TIoK2f6trPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-darwin-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-darwin-x64/-/tar-darwin-x64-1.1.0.tgz", + "integrity": "sha512-EI+CptIMNweT0ms9S3mkP/q+J6FNZ1Q6pvpJOEcWglRfyfQpLqjlC0O+dptruTPE8VamKYuqdjxfqD8hifZDOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-freebsd-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-freebsd-x64/-/tar-freebsd-x64-1.1.0.tgz", + "integrity": "sha512-J0PIqX+pl6lBIAckL/c87gpodLbjZB1OtIK+RDscKC9NLdpVv6VGOxzUV/fYev/hctcE8EfkLbgFOfpmVQPg2g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-linux-arm-gnueabihf": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-arm-gnueabihf/-/tar-linux-arm-gnueabihf-1.1.0.tgz", + "integrity": "sha512-SLgIQo3f3EjkZ82ZwvrEgFvMdDAhsxCYjyoSuWfHCz0U16qx3SuGCp8+FYOPYCECHN3ZlGjXnoAIt9ERd0dEUg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-linux-arm64-gnu": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-arm64-gnu/-/tar-linux-arm64-gnu-1.1.0.tgz", + "integrity": "sha512-d014cdle52EGaH6GpYTQOP9Py7glMO1zz/+ynJPjjzYFSxvdYx0byrjumZk2UQdIyGZiJO2MEFpCkEEKFSgPYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-linux-arm64-musl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-arm64-musl/-/tar-linux-arm64-musl-1.1.0.tgz", + "integrity": "sha512-L/y1/26q9L/uBqiW/JdOb/Dc94egFvNALUZV2WCGKQXc6UByPBMgdiEyW2dtoYxYYYYc+AKD+jr+wQPcvX2vrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-linux-ppc64-gnu": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-ppc64-gnu/-/tar-linux-ppc64-gnu-1.1.0.tgz", + "integrity": "sha512-EPE1K/80RQvPbLRJDJs1QmCIcH+7WRi0F73+oTe1582y9RtfGRuzAkzeBuAGRXAQEjRQw/RjtNqr6UTJ+8UuWQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-linux-s390x-gnu": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-s390x-gnu/-/tar-linux-s390x-gnu-1.1.0.tgz", + "integrity": "sha512-B2jhWiB1ffw1nQBqLUP1h4+J1ovAxBOoe5N2IqDMOc63fsPZKNqF1PvO/dIem8z7LL4U4bsfmhy3gBfu547oNQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-linux-x64-gnu": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-x64-gnu/-/tar-linux-x64-gnu-1.1.0.tgz", + "integrity": "sha512-tbZDHnb9617lTnsDMGo/eAMZxnsQFnaRe+MszRqHguKfMwkisc9CCJnks/r1o84u5fECI+J/HOrKXgczq/3Oww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-linux-x64-musl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-x64-musl/-/tar-linux-x64-musl-1.1.0.tgz", + "integrity": "sha512-dV6cODlzbO8u6Anmv2N/ilQHq/AWz0xyltuXoLU3yUyXbZcnWYZuB2rL8OBGPmqNcD+x9NdScBNXh7vWN0naSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-wasm32-wasi": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-wasm32-wasi/-/tar-wasm32-wasi-1.1.0.tgz", + "integrity": "sha512-jIa9nb2HzOrfH0F8QQ9g3WE4aMH5vSI5/1NYVNm9ysCmNjCCtMXCAhlI3WKCdm/DwHf0zLqdrrtDFXODcNaqMw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@napi-rs/tar-win32-arm64-msvc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-win32-arm64-msvc/-/tar-win32-arm64-msvc-1.1.0.tgz", + "integrity": "sha512-vfpG71OB0ijtjemp3WTdmBKJm9R70KM8vsSExMsIQtV0lVzP07oM1CW6JbNRPXNLhRoue9ofYLiUDk8bE0Hckg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-win32-ia32-msvc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-win32-ia32-msvc/-/tar-win32-ia32-msvc-1.1.0.tgz", + "integrity": "sha512-hGPyPW60YSpOSgzfy68DLBHgi6HxkAM+L59ZZZPMQ0TOXjQg+p2EW87+TjZfJOkSpbYiEkULwa/f4a2hcVjsqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/tar-win32-x64-msvc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/tar-win32-x64-msvc/-/tar-win32-x64-msvc-1.1.0.tgz", + "integrity": "sha512-L6Ed1DxXK9YSCMyvpR8MiNAyKNkQLjsHsHK9E0qnHa8NzLFqzDKhvs5LfnWxM2kJ+F7m/e5n9zPm24kHb3LsVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/wasm-tools": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools/-/wasm-tools-1.0.1.tgz", + "integrity": "sha512-enkZYyuCdo+9jneCPE/0fjIta4wWnvVN9hBo2HuiMpRF0q3lzv1J6b/cl7i0mxZUKhBrV3aCKDBQnCOhwKbPmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/wasm-tools-android-arm-eabi": "1.0.1", + "@napi-rs/wasm-tools-android-arm64": "1.0.1", + "@napi-rs/wasm-tools-darwin-arm64": "1.0.1", + "@napi-rs/wasm-tools-darwin-x64": "1.0.1", + "@napi-rs/wasm-tools-freebsd-x64": "1.0.1", + "@napi-rs/wasm-tools-linux-arm64-gnu": "1.0.1", + "@napi-rs/wasm-tools-linux-arm64-musl": "1.0.1", + "@napi-rs/wasm-tools-linux-x64-gnu": "1.0.1", + "@napi-rs/wasm-tools-linux-x64-musl": "1.0.1", + "@napi-rs/wasm-tools-wasm32-wasi": "1.0.1", + "@napi-rs/wasm-tools-win32-arm64-msvc": "1.0.1", + "@napi-rs/wasm-tools-win32-ia32-msvc": "1.0.1", + "@napi-rs/wasm-tools-win32-x64-msvc": "1.0.1" + } + }, + "node_modules/@napi-rs/wasm-tools-android-arm-eabi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-android-arm-eabi/-/wasm-tools-android-arm-eabi-1.0.1.tgz", + "integrity": "sha512-lr07E/l571Gft5v4aA1dI8koJEmF1F0UigBbsqg9OWNzg80H3lDPO+auv85y3T/NHE3GirDk7x/D3sLO57vayw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-android-arm64/-/wasm-tools-android-arm64-1.0.1.tgz", + "integrity": "sha512-WDR7S+aRLV6LtBJAg5fmjKkTZIdrEnnQxgdsb7Cf8pYiMWBHLU+LC49OUVppQ2YSPY0+GeYm9yuZWW3kLjJ7Bg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-darwin-arm64/-/wasm-tools-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-qWTI+EEkiN0oIn/N2gQo7+TVYil+AJ20jjuzD2vATS6uIjVz+Updeqmszi7zq7rdFTLp6Ea3/z4kDKIfZwmR9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-darwin-x64/-/wasm-tools-darwin-x64-1.0.1.tgz", + "integrity": "sha512-bA6hubqtHROR5UI3tToAF/c6TDmaAgF0SWgo4rADHtQ4wdn0JeogvOk50gs2TYVhKPE2ZD2+qqt7oBKB+sxW3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-freebsd-x64/-/wasm-tools-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-90+KLBkD9hZEjPQW1MDfwSt5J1L46EUKacpCZWyRuL6iIEO5CgWU0V/JnEgFsDOGyyYtiTvHc5bUdUTWd4I9Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-arm64-gnu/-/wasm-tools-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-rG0QlS65x9K/u3HrKafDf8cFKj5wV2JHGfl8abWgKew0GVPyp6vfsDweOwHbWAjcHtp2LHi6JHoW80/MTHm52Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-arm64-musl/-/wasm-tools-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-jAasbIvjZXCgX0TCuEFQr+4D6Lla/3AAVx2LmDuMjgG4xoIXzjKWl7c4chuaD+TI+prWT0X6LJcdzFT+ROKGHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-x64-gnu/-/wasm-tools-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-Plgk5rPqqK2nocBGajkMVbGm010Z7dnUgq0wtnYRZbzWWxwWcXfZMPa8EYxrK4eE8SzpI7VlZP1tdVsdjgGwMw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-x64-musl/-/wasm-tools-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-GW7AzGuWxtQkyHknHWYFdR0CHmW6is8rG2Rf4V6GNmMpmwtXt/ItWYWtBe4zqJWycMNazpfZKSw/BpT7/MVCXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-wasm32-wasi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-wasm32-wasi/-/wasm-tools-wasm32-wasi-1.0.1.tgz", + "integrity": "sha512-/nQVSTrqSsn7YdAc2R7Ips/tnw5SPUcl3D7QrXCNGPqjbatIspnaexvaOYNyKMU6xPu+pc0BTnKVmqhlJJCPLA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@napi-rs/wasm-tools-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-win32-arm64-msvc/-/wasm-tools-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-PFi7oJIBu5w7Qzh3dwFea3sHRO3pojMsaEnUIy22QvsW+UJfNQwJCryVrpoUt8m4QyZXI+saEq/0r4GwdoHYFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-win32-ia32-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-win32-ia32-msvc/-/wasm-tools-win32-ia32-msvc-1.0.1.tgz", + "integrity": "sha512-gXkuYzxQsgkj05Zaq+KQTkHIN83dFAwMcTKa2aQcpYPRImFm2AQzEyLtpXmyCWzJ0F9ZYAOmbSyrNew8/us6bw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-tools-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-win32-x64-msvc/-/wasm-tools-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-rEAf05nol3e3eei2sRButmgXP+6ATgm0/38MKhz9Isne82T4rPIMYsCIFj0kOisaGeVwoi2fnm7O9oWp5YVnYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz", + "integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz", + "integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.3", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "json-with-bigint": "^3.5.3", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/rest": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/@oxlint/darwin-arm64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-0.16.12.tgz", + "integrity": "sha512-G7phYhlIA4ke2nW7tHLl+E5+rvdzgGA6830D+e+y1RGllT0w2ONGdKcVTj+2pXGCw6yPmCC5fDsDEn2+RPTfxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/darwin-x64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-0.16.12.tgz", + "integrity": "sha512-P/LSOgJ6SzQ3OKEIf3HsebgokZiZ5nDuTgIL4LpNCHlkOLDu/fT8XL9pSkR5y+60v0SOxUF/+aN0Q8EmxblrCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/linux-arm64-gnu": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-0.16.12.tgz", + "integrity": "sha512-0N/ZsW+cL7ZAUvOHbzMp3iApt5b/Q81q2e9RgEzkI6gUDCJK8/blWg0se/i6y9e24WH0ZC4bcxY1+Qz4ZQ+mFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-arm64-musl": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-0.16.12.tgz", + "integrity": "sha512-MoG1SIw4RGowsOsPjm5HjRWymisRZWBea7ewMoXA5xIVQ3eqECifG0KJW0OZp96Ad8DFBEavdlNuImB2uXsMwg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-gnu": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-0.16.12.tgz", + "integrity": "sha512-STho8QdMLfn/0lqRU94tGPaYX8lGJccPbqeUcEr3eK5gZ5ZBdXmiHlvkcngXFEXksYC8/5VoJN7Vf3HsmkEskw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-musl": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-0.16.12.tgz", + "integrity": "sha512-i7pzSoj9nCg/ZzOe8dCZeFWyRRWDylR9tIX04xRTq3G6PBLm6i9VrOdEkxbgM9+pCkRzUc0a9D7rbtCF34TQUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/win32-arm64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-0.16.12.tgz", + "integrity": "sha512-wcxq3IBJ7ZlONlXJxQM+7EMx+LX1nkz3ZS3R0EtDM76EOZaqe8BMkW5cXVhF8jarZTZC18oKAckW4Ng9d8adBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/win32-x64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-0.16.12.tgz", + "integrity": "sha512-Ae1fx7wmAcMVqzS8rLINaFRpAdh29QzHh133bEYMHzfWBYyK/hLu9g4GLwC/lEIVQu9884b8qutGfdOk6Qia3w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@vercel/nft": { + "version": "0.29.4", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.29.4.tgz", + "integrity": "sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^10.4.5", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ava": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/ava/-/ava-6.4.1.tgz", + "integrity": "sha512-vxmPbi1gZx9zhAjHBgw81w/iEDKcrokeRk/fqDTyA2DQygZ0o+dUGRHFOtX8RA5N0heGJTTsIk7+xYxitDb61Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vercel/nft": "^0.29.4", + "acorn": "^8.15.0", + "acorn-walk": "^8.3.4", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.2.0", + "cbor": "^10.0.9", + "chalk": "^5.4.1", + "chunkd": "^2.0.1", + "ci-info": "^4.3.0", + "ci-parallel-vars": "^1.0.1", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.4.1", + "emittery": "^1.2.0", + "figures": "^6.1.0", + "globby": "^14.1.0", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "memoize": "^10.1.0", + "ms": "^2.1.3", + "p-map": "^7.0.3", + "package-config": "^5.0.0", + "picomatch": "^4.0.2", + "plur": "^5.1.0", + "pretty-ms": "^9.2.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.1.0", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^6.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^22 || ^23 || >=24" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cbor": { + "version": "10.0.12", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-10.0.12.tgz", + "integrity": "sha512-exQDevYd7ZQLP4moMQcZkKCVZsXLAtUSflObr3xTh4xzFIv/xBCdvCd6L259kQOUP2kcTC0jvC6PpZIf/WmRXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "nofilter": "^3.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/clipanion": { + "version": "4.0.0-rc.4", + "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-4.0.0-rc.4.tgz", + "integrity": "sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q==", + "dev": true, + "license": "MIT", + "workspaces": [ + "website" + ], + "dependencies": { + "typanion": "^3.8.0" + }, + "peerDependencies": { + "typanion": "*" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, + "node_modules/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emittery": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.2.1.tgz", + "integrity": "sha512-sFz64DCRjirhwHLxofFqxYQm6DCp6o0Ix7jwKQvuCHPn4GMRZNuBZyLPu9Ccmk/QSCAMZt6FOUqA8JZCQvA9fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emnapi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/emnapi/-/emnapi-1.9.0.tgz", + "integrity": "sha512-o/MgVYc9Xa92jNd5pk+PRtFx/IuWlILW1vqm9UU7V49Cnos+bK2y982HEtXcRgkdtxZylYBaGWEznC9Alo6xhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "node-addon-api": ">= 6.1.0" + }, + "peerDependenciesMeta": { + "node-addon-api": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-toolkit": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz", + "integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz", + "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-with-bigint": { + "version": "3.5.7", + "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.7.tgz", + "integrity": "sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "license": "MIT", + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/memoize": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.2.0.tgz", + "integrity": "sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/oxlint": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-0.16.12.tgz", + "integrity": "sha512-1oN3P9bzE90zkbjLTc+uICVLwSR+eQaDaYVipS0BtmtmEd3ccQue0y7npCinb35YqKzIv1LZxhoU9nm5fgmQuw==", + "dev": true, + "license": "MIT", + "bin": { + "oxc_language_server": "bin/oxc_language_server", + "oxlint": "bin/oxlint" + }, + "engines": { + "node": ">=8.*" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/darwin-arm64": "0.16.12", + "@oxlint/darwin-x64": "0.16.12", + "@oxlint/linux-arm64-gnu": "0.16.12", + "@oxlint/linux-arm64-musl": "0.16.12", + "@oxlint/linux-x64-gnu": "0.16.12", + "@oxlint/linux-x64-musl": "0.16.12", + "@oxlint/win32-arm64": "0.16.12", + "@oxlint/win32-x64": "0.16.12" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz", + "integrity": "sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "load-json-file": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/supertap/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/supertap/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/tar": { + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", + "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typanion": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz", + "integrity": "sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==", + "dev": true, + "license": "MIT", + "workspaces": [ + "website" + ] + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/write-file-atomic": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/crates/bashkit-js/package.json b/crates/bashkit-js/package.json new file mode 100644 index 00000000..59eb4888 --- /dev/null +++ b/crates/bashkit-js/package.json @@ -0,0 +1,76 @@ +{ + "name": "@everruns/bashkit", + "version": "0.1.9", + "description": "Sandboxed bash interpreter for JavaScript/TypeScript", + "main": "wrapper.js", + "types": "wrapper.d.ts", + "type": "module", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/everruns/bashkit" + }, + "keywords": [ + "bash", + "interpreter", + "sandbox", + "ai", + "agent", + "tool" + ], + "napi": { + "binaryName": "bashkit", + "targets": [ + "x86_64-apple-darwin", + "aarch64-apple-darwin", + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + "x86_64-pc-windows-msvc", + "wasm32-wasip1-threads" + ] + }, + "files": [ + "wrapper.js", + "wrapper.d.ts", + "index.cjs", + "index.d.ts", + "index.d.cts", + "bashkit.*.node", + "bashkit.*.wasm" + ], + "scripts": { + "build": "npm run build:napi && npm run build:cjs && npm run build:ts", + "build:napi": "napi build --platform --release", + "build:cjs": "node -e \"const fs=require('fs'); if(fs.existsSync('index.js')){fs.renameSync('index.js','index.cjs')}; if(fs.existsSync('index.d.ts')){fs.copyFileSync('index.d.ts','index.d.cts')}\"", + "build:ts": "tsc", + "artifacts": "napi artifacts", + "test": "ava", + "lint": "oxlint .", + "format": "prettier --write .", + "format:check": "prettier --check .", + "type-check": "tsc --noEmit", + "version": "napi version" + }, + "devDependencies": { + "@napi-rs/cli": "^3.0.0", + "@types/node": "^25.5.0", + "ava": "^6.2.0", + "oxlint": "^0.16.0", + "prettier": "^3.4.0", + "tsx": "^4.21.0", + "typescript": "^5.7.0" + }, + "ava": { + "extensions": { + "ts": "module" + }, + "timeout": "2m", + "workerThreads": false, + "nodeArguments": [ + "--import=tsx" + ] + }, + "engines": { + "node": ">= 18" + } +} diff --git a/crates/bashkit-js/scripts/smoke-test.sh b/crates/bashkit-js/scripts/smoke-test.sh new file mode 100755 index 00000000..4c0605cd --- /dev/null +++ b/crates/bashkit-js/scripts/smoke-test.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Smoke test: build, pack, install in isolated dir, run tests +set -e + +cd "$(dirname "$0")/.." + +echo "=== Building ===" +npm run build + +# Find the .node file +NODE_FILE=$(ls bashkit.*.node 2>/dev/null | head -1) +if [ -z "$NODE_FILE" ]; then + echo "ERROR: No .node file found after build" + exit 1 +fi +echo "Built: $NODE_FILE" + +# Extract platform from filename (e.g., bashkit.darwin-arm64.node -> darwin-arm64) +PLATFORM=$(echo "$NODE_FILE" | sed 's/bashkit\.\(.*\)\.node/\1/') +echo "Platform: $PLATFORM" + +echo "=== Creating npm dirs ===" +npm run create-npm-dirs + +# Copy binary to platform package +PLATFORM_DIR="npm/${PLATFORM}" +if [ ! -d "$PLATFORM_DIR" ]; then + echo "ERROR: Platform dir $PLATFORM_DIR not found" + exit 1 +fi +cp "$NODE_FILE" "$PLATFORM_DIR/" + +echo "=== Preparing packages ===" +npx napi prepublish --skip-gh-release + +# Pack platform package +PLATFORM_TGZ=$(cd "$PLATFORM_DIR" && npm pack 2>/dev/null | tail -1) +PLATFORM_TGZ_PATH="$(cd "$PLATFORM_DIR" && pwd)/$PLATFORM_TGZ" + +# Pack main package +MAIN_TGZ=$(npm pack 2>/dev/null | tail -1) +MAIN_TGZ_PATH="$(pwd)/$MAIN_TGZ" + +echo "=== Installing in smoke-test dir ===" +SMOKE_DIR="smoke-test" +rm -rf "$SMOKE_DIR" +mkdir -p "$SMOKE_DIR" +cd "$SMOKE_DIR" + +npm init -y > /dev/null 2>&1 +npm install "$PLATFORM_TGZ_PATH" "$MAIN_TGZ_PATH" + +echo "=== Running smoke test ===" +node -e " +const { BashTool, getVersion } = require('@everruns/bashkit'); +const tool = new BashTool(); +console.log('Version:', getVersion()); +const r = tool.executeSync('echo hello'); +console.log('stdout:', JSON.stringify(r.stdout)); +console.assert(r.exit_code === 0, 'Expected exit_code 0'); +console.assert(r.stdout.trim() === 'hello', 'Expected hello'); +console.log('Smoke test PASSED'); +" + +echo "=== Cleanup ===" +cd .. +rm -rf smoke-test npm +rm -f bashkit.*.node *.tgz +# Restore any package.json changes from napi prepublish +git checkout package.json 2>/dev/null || true + +echo "=== Done ===" diff --git a/crates/bashkit-js/src/lib.rs b/crates/bashkit-js/src/lib.rs new file mode 100644 index 00000000..5532a261 --- /dev/null +++ b/crates/bashkit-js/src/lib.rs @@ -0,0 +1,325 @@ +// napi macros generate code that triggers some clippy lints +#![allow(clippy::needless_pass_by_value, clippy::trivially_copy_pass_by_ref)] + +//! Node.js/TypeScript bindings for the Bashkit sandboxed bash interpreter. +//! +//! Exposes `Bash` (core interpreter), `BashTool` (interpreter + LLM metadata), +//! and `ExecResult` via napi-rs for use from JavaScript/TypeScript. + +use bashkit::tool::VERSION; +use bashkit::{Bash as RustBash, BashTool as RustBashTool, ExecutionLimits, Tool}; +use napi_derive::napi; +use std::sync::Arc; +use tokio::sync::Mutex; + +// ============================================================================ +// ExecResult +// ============================================================================ + +/// Result from executing bash commands. +#[napi(object)] +#[derive(Clone)] +pub struct ExecResult { + pub stdout: String, + pub stderr: String, + pub exit_code: i32, + pub error: Option, +} + +// ============================================================================ +// BashOptions +// ============================================================================ + +/// Options for creating a Bash or BashTool instance. +#[napi(object)] +pub struct BashOptions { + pub username: Option, + pub hostname: Option, + pub max_commands: Option, + pub max_loop_iterations: Option, +} + +// ============================================================================ +// Bash — core interpreter +// ============================================================================ + +/// Core bash interpreter with virtual filesystem. +/// +/// State persists between calls — files created in one `execute()` are +/// available in subsequent calls. +#[napi] +pub struct Bash { + inner: Arc>, + rt: tokio::runtime::Runtime, + username: Option, + hostname: Option, + max_commands: Option, + max_loop_iterations: Option, +} + +#[napi] +impl Bash { + #[napi(constructor)] + pub fn new(options: Option) -> napi::Result { + let opts = options.unwrap_or(BashOptions { + username: None, + hostname: None, + max_commands: None, + max_loop_iterations: None, + }); + + let bash = build_bash( + opts.username.as_deref(), + opts.hostname.as_deref(), + opts.max_commands, + opts.max_loop_iterations, + ); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|e| napi::Error::from_reason(format!("Failed to create runtime: {e}")))?; + + Ok(Self { + inner: Arc::new(Mutex::new(bash)), + rt, + username: opts.username, + hostname: opts.hostname, + max_commands: opts.max_commands, + max_loop_iterations: opts.max_loop_iterations, + }) + } + + /// Execute bash commands synchronously. + #[napi] + pub fn execute_sync(&self, commands: String) -> napi::Result { + let inner = self.inner.clone(); + self.rt.block_on(async move { + let mut bash = inner.lock().await; + match bash.exec(&commands).await { + Ok(result) => Ok(ExecResult { + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exit_code, + error: None, + }), + Err(e) => Ok(ExecResult { + stdout: String::new(), + stderr: String::new(), + exit_code: 1, + error: Some(e.to_string()), + }), + } + }) + } + + /// Reset interpreter to fresh state, preserving configuration. + #[napi] + pub fn reset(&self) -> napi::Result<()> { + let inner = self.inner.clone(); + let username = self.username.clone(); + let hostname = self.hostname.clone(); + let max_commands = self.max_commands; + let max_loop_iterations = self.max_loop_iterations; + + self.rt.block_on(async move { + let mut bash = inner.lock().await; + *bash = build_bash( + username.as_deref(), + hostname.as_deref(), + max_commands, + max_loop_iterations, + ); + Ok(()) + }) + } +} + +// ============================================================================ +// BashTool — interpreter + LLM tool metadata +// ============================================================================ + +/// Bash interpreter with LLM tool metadata (schema, description, system_prompt). +/// +/// Use this when integrating with AI frameworks that need tool definitions. +#[napi] +pub struct BashTool { + inner: Arc>, + rt: tokio::runtime::Runtime, + username: Option, + hostname: Option, + max_commands: Option, + max_loop_iterations: Option, +} + +#[napi] +impl BashTool { + #[napi(constructor)] + pub fn new(options: Option) -> napi::Result { + let opts = options.unwrap_or(BashOptions { + username: None, + hostname: None, + max_commands: None, + max_loop_iterations: None, + }); + + let bash = build_bash( + opts.username.as_deref(), + opts.hostname.as_deref(), + opts.max_commands, + opts.max_loop_iterations, + ); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|e| napi::Error::from_reason(format!("Failed to create runtime: {e}")))?; + + Ok(Self { + inner: Arc::new(Mutex::new(bash)), + rt, + username: opts.username, + hostname: opts.hostname, + max_commands: opts.max_commands, + max_loop_iterations: opts.max_loop_iterations, + }) + } + + /// Execute bash commands synchronously. + #[napi] + pub fn execute_sync(&self, commands: String) -> napi::Result { + let inner = self.inner.clone(); + self.rt.block_on(async move { + let mut bash = inner.lock().await; + match bash.exec(&commands).await { + Ok(result) => Ok(ExecResult { + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exit_code, + error: None, + }), + Err(e) => Ok(ExecResult { + stdout: String::new(), + stderr: String::new(), + exit_code: 1, + error: Some(e.to_string()), + }), + } + }) + } + + /// Reset interpreter to fresh state, preserving configuration. + #[napi] + pub fn reset(&self) -> napi::Result<()> { + let inner = self.inner.clone(); + let username = self.username.clone(); + let hostname = self.hostname.clone(); + let max_commands = self.max_commands; + let max_loop_iterations = self.max_loop_iterations; + + self.rt.block_on(async move { + let mut bash = inner.lock().await; + *bash = build_bash( + username.as_deref(), + hostname.as_deref(), + max_commands, + max_loop_iterations, + ); + Ok(()) + }) + } + + /// Get tool name. + #[napi(getter)] + pub fn name(&self) -> &str { + "bashkit" + } + + /// Get short description. + #[napi(getter)] + pub fn short_description(&self) -> &str { + "Virtual bash interpreter with virtual filesystem" + } + + /// Get full description. + #[napi] + pub fn description(&self) -> String { + let tool = RustBashTool::default(); + tool.description() + } + + /// Get help text. + #[napi] + pub fn help(&self) -> String { + let tool = RustBashTool::default(); + tool.help() + } + + /// Get system prompt for LLMs. + #[napi] + pub fn system_prompt(&self) -> String { + let tool = RustBashTool::default(); + tool.system_prompt() + } + + /// Get JSON input schema as string. + #[napi] + pub fn input_schema(&self) -> napi::Result { + let tool = RustBashTool::default(); + let schema = tool.input_schema(); + serde_json::to_string_pretty(&schema) + .map_err(|e| napi::Error::from_reason(format!("Schema serialization failed: {e}"))) + } + + /// Get JSON output schema as string. + #[napi] + pub fn output_schema(&self) -> napi::Result { + let tool = RustBashTool::default(); + let schema = tool.output_schema(); + serde_json::to_string_pretty(&schema) + .map_err(|e| napi::Error::from_reason(format!("Schema serialization failed: {e}"))) + } + + /// Get tool version. + #[napi(getter)] + pub fn version(&self) -> &str { + VERSION + } +} + +// ============================================================================ +// Helpers +// ============================================================================ + +fn build_bash( + username: Option<&str>, + hostname: Option<&str>, + max_commands: Option, + max_loop_iterations: Option, +) -> RustBash { + let mut builder = RustBash::builder(); + + if let Some(u) = username { + builder = builder.username(u); + } + if let Some(h) = hostname { + builder = builder.hostname(h); + } + + let mut limits = ExecutionLimits::new(); + if let Some(mc) = max_commands { + limits = limits.max_commands(mc as usize); + } + if let Some(mli) = max_loop_iterations { + limits = limits.max_loop_iterations(mli as usize); + } + builder = builder.limits(limits); + + builder.build() +} + +/// Get the bashkit version string. +#[napi] +pub fn get_version() -> &'static str { + VERSION +} diff --git a/crates/bashkit-js/tsconfig.json b/crates/bashkit-js/tsconfig.json new file mode 100644 index 00000000..8ba29be6 --- /dev/null +++ b/crates/bashkit-js/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": ["wrapper.ts"], + "exclude": ["node_modules", "__test__"] +} diff --git a/crates/bashkit-js/wrapper.ts b/crates/bashkit-js/wrapper.ts new file mode 100644 index 00000000..66a02c2d --- /dev/null +++ b/crates/bashkit-js/wrapper.ts @@ -0,0 +1,180 @@ +import { createRequire } from "node:module"; +import type { + Bash as NativeBashType, + BashTool as NativeBashToolType, + ExecResult, + BashOptions, +} from "./index.cjs"; + +const require = createRequire(import.meta.url); +const native = require("./index.cjs"); +const NativeBash: typeof NativeBashType = native.Bash; +const NativeBashTool: typeof NativeBashToolType = native.BashTool; +const nativeGetVersion: () => string = native.getVersion; + +export type { ExecResult, BashOptions }; + +/** + * Error thrown when a bash command execution fails. + */ +export class BashError extends Error { + readonly exitCode: number; + readonly stderr: string; + + constructor(result: ExecResult) { + const message = result.error ?? result.stderr ?? `Exit code ${result.exitCode}`; + super(message); + this.name = "BashError"; + this.exitCode = result.exitCode; + this.stderr = result.stderr; + } + + display(): string { + return `BashError(exit_code=${this.exitCode}): ${this.message}`; + } +} + +/** + * Core bash interpreter with virtual filesystem. + * + * State persists between calls — files created in one `execute()` are + * available in subsequent calls. + * + * @example + * ```typescript + * import { Bash } from '@everruns/bashkit'; + * + * const bash = new Bash(); + * const result = bash.executeSync('echo "Hello, World!"'); + * console.log(result.stdout); // Hello, World!\n + * ``` + */ +export class Bash { + private native: NativeBashType; + + constructor(options?: BashOptions) { + this.native = new NativeBash(options); + } + + /** + * Execute bash commands synchronously and return the result. + */ + executeSync(commands: string): ExecResult { + return this.native.executeSync(commands); + } + + /** + * Execute bash commands synchronously. Throws `BashError` on non-zero exit. + */ + executeSyncOrThrow(commands: string): ExecResult { + const result = this.native.executeSync(commands); + if (result.exitCode !== 0) { + throw new BashError(result); + } + return result; + } + + /** + * Reset interpreter to fresh state, preserving configuration. + */ + reset(): void { + this.native.reset(); + } +} + +/** + * Bash interpreter with LLM tool metadata (schema, description, system_prompt). + * + * Use this when integrating with AI frameworks that need tool definitions. + * + * @example + * ```typescript + * import { BashTool } from '@everruns/bashkit'; + * + * const tool = new BashTool(); + * console.log(tool.name); // "bashkit" + * console.log(tool.inputSchema()); // JSON schema string + * + * const result = tool.executeSync('echo hello'); + * console.log(result.stdout); // hello\n + * ``` + */ +export class BashTool { + private native: NativeBashToolType; + + constructor(options?: BashOptions) { + this.native = new NativeBashTool(options); + } + + /** + * Execute bash commands synchronously and return the result. + */ + executeSync(commands: string): ExecResult { + return this.native.executeSync(commands); + } + + /** + * Execute bash commands synchronously. Throws `BashError` on non-zero exit. + */ + executeSyncOrThrow(commands: string): ExecResult { + const result = this.native.executeSync(commands); + if (result.exitCode !== 0) { + throw new BashError(result); + } + return result; + } + + /** + * Reset interpreter to fresh state, preserving configuration. + */ + reset(): void { + this.native.reset(); + } + + /** Tool name. */ + get name(): string { + return this.native.name; + } + + /** Short description. */ + get shortDescription(): string { + return this.native.shortDescription; + } + + /** Full description. */ + description(): string { + return this.native.description(); + } + + /** Help text. */ + help(): string { + return this.native.help(); + } + + /** System prompt for LLMs. */ + systemPrompt(): string { + return this.native.systemPrompt(); + } + + /** JSON input schema as string. */ + inputSchema(): string { + return this.native.inputSchema(); + } + + /** JSON output schema as string. */ + outputSchema(): string { + return this.native.outputSchema(); + } + + /** Tool version. */ + get version(): string { + return this.native.version; + } +} + +/** + * Get the bashkit version string. + */ +export function getVersion(): string { + return nativeGetVersion(); +} diff --git a/examples/README.md b/examples/README.md index 7d6edb27..cd4ebb1c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,12 +10,12 @@ cargo build -p bashkit-cli --features realfs bash examples/realfs_mount.sh ``` -## Python Examples +## Python Python examples use [PEP 723](https://peps.python.org/pep-0723/) inline script metadata. `uv run` resolves dependencies automatically — bashkit installs from PyPI as a pre-built wheel (no Rust toolchain needed). -## treasure_hunt_agent.py +### treasure_hunt_agent.py LangChain agent with Bashkit sandbox. @@ -24,7 +24,7 @@ export ANTHROPIC_API_KEY=your_key uv run examples/treasure_hunt_agent.py ``` -## deepagent_coding_agent.py +### deepagent_coding_agent.py Deep Agents with Bashkit middleware + backend. @@ -32,3 +32,70 @@ Deep Agents with Bashkit middleware + backend. export ANTHROPIC_API_KEY=your_key uv run examples/deepagent_coding_agent.py ``` + +## JavaScript / TypeScript + +JS examples import `@everruns/bashkit`. Install from npm or build locally: + +```bash +# From npm +npm install @everruns/bashkit + +# Or build locally +cd crates/bashkit-js && npm install && npm run build +# Then run with NODE_PATH=crates/bashkit-js +``` + +### bash_basics.mjs + +Core features: execution, pipelines, variables, loops, jq, error handling, reset. + +```bash +node examples/bash_basics.mjs +``` + +### data_pipeline.mjs + +Real-world data tasks: CSV processing, JSON transformation, log analysis, report generation. + +```bash +node examples/data_pipeline.mjs +``` + +### llm_tool.mjs + +Wire BashTool into any AI framework: tool definition, simulated tool-call loop, generic adapter. + +```bash +node examples/llm_tool.mjs +``` + +### openai_tool.mjs + +OpenAI function calling with manual tool-call loop. + +```bash +npm install openai +export OPENAI_API_KEY=sk-... +node examples/openai_tool.mjs +``` + +### vercel_ai_tool.mjs + +Vercel AI SDK `tool()` + `generateText()` with automatic tool-call loop. + +```bash +npm install ai @ai-sdk/openai zod +export OPENAI_API_KEY=sk-... +node examples/vercel_ai_tool.mjs +``` + +### langchain_agent.mjs + +LangChain.js ReAct agent with `DynamicStructuredTool`. + +```bash +npm install @langchain/core @langchain/langgraph @langchain/openai zod +export OPENAI_API_KEY=sk-... +node examples/langchain_agent.mjs +``` diff --git a/examples/bash_basics.mjs b/examples/bash_basics.mjs new file mode 100644 index 00000000..8f97bf60 --- /dev/null +++ b/examples/bash_basics.mjs @@ -0,0 +1,181 @@ +#!/usr/bin/env node +/** + * Basic usage of the Bash and BashTool interfaces. + * + * Demonstrates core features: command execution, pipelines, variables, + * loops, virtual filesystem persistence, jq, error handling, and reset. + * + * Run: + * node examples/bash_basics.mjs + */ + +import { Bash, BashTool, getVersion, BashError } from "@everruns/bashkit"; + +function demoBasics() { + console.log("=== Basic Execution ===\n"); + + const bash = new Bash(); + + // Simple command + const r1 = bash.executeSync('echo "Hello from Bashkit!"'); + console.log(`echo: ${r1.stdout.trim()}`); + assert(r1.exitCode === 0); + + // Pipeline + const r2 = bash.executeSync("echo -e 'banana\\napple\\ncherry' | sort"); + console.log(`sort: ${r2.stdout.trim()}`); + assert(r2.stdout.trim() === "apple\nbanana\ncherry"); + + // Arithmetic + const r3 = bash.executeSync("echo $((10 * 5 - 3))"); + console.log(`math: ${r3.stdout.trim()}`); + assert(r3.stdout.trim() === "47"); + + console.log(); +} + +function demoState() { + console.log("=== State Persistence ===\n"); + + const bash = new Bash(); + + // Variables persist across calls + bash.executeSync("MY_VAR='persistent'"); + const r1 = bash.executeSync("echo $MY_VAR"); + console.log(`var: ${r1.stdout.trim()}`); + assert(r1.stdout.trim() === "persistent"); + + // Virtual filesystem persists + bash.executeSync("mkdir -p /tmp/demo && echo 'data' > /tmp/demo/file.txt"); + const r2 = bash.executeSync("cat /tmp/demo/file.txt"); + console.log(`file: ${r2.stdout.trim()}`); + assert(r2.stdout.trim() === "data"); + + // Loops and arithmetic + const r3 = bash.executeSync(` + total=0 + for i in 1 2 3 4 5; do + total=$((total + i)) + done + echo $total + `); + console.log(`sum: ${r3.stdout.trim()}`); + assert(r3.stdout.trim() === "15"); + + // Reset clears state + bash.reset(); + const r4 = bash.executeSync("echo ${MY_VAR:-unset}"); + console.log(`reset: ${r4.stdout.trim()}`); + assert(r4.stdout.trim() === "unset"); + + console.log(); +} + +function demoErrorHandling() { + console.log("=== Error Handling ===\n"); + + const bash = new Bash(); + + // Exit codes + const r1 = bash.executeSync("exit 42"); + console.log(`exit: code=${r1.exitCode}`); + assert(r1.exitCode === 42); + + // executeSyncOrThrow + try { + bash.executeSyncOrThrow("exit 1"); + assert(false, "should have thrown"); + } catch (e) { + console.log(`thrown: ${e instanceof BashError ? "BashError" : "other"}`); + assert(e instanceof BashError); + console.log(`display: ${e.display()}`); + } + + // Recover after error + const r2 = bash.executeSync("echo 'recovered'"); + console.log(`recovered: ${r2.stdout.trim()}`); + assert(r2.exitCode === 0); + + console.log(); +} + +function demoJq() { + console.log("=== JSON Processing (jq) ===\n"); + + const bash = new Bash(); + + // Create JSON data + bash.executeSync(`cat > /tmp/users.json << 'EOF' +[ + {"name": "Alice", "role": "admin"}, + {"name": "Bob", "role": "user"}, + {"name": "Carol", "role": "admin"} +] +EOF`); + + // Query with jq + const r1 = bash.executeSync( + 'cat /tmp/users.json | jq \'[.[] | select(.role == "admin")] | length\'' + ); + console.log(`admins: ${r1.stdout.trim()}`); + assert(r1.stdout.trim() === "2"); + + // Extract names + const r2 = bash.executeSync( + "cat /tmp/users.json | jq -r '.[].name' | sort" + ); + console.log(`names: ${r2.stdout.trim()}`); + assert(r2.stdout.trim() === "Alice\nBob\nCarol"); + + console.log(); +} + +function demoConfig() { + console.log("=== Configuration ===\n"); + + const bash = new Bash({ username: "agent", hostname: "sandbox" }); + console.log(`whoami: ${bash.executeSync("whoami").stdout.trim()}`); + console.log(`hostname: ${bash.executeSync("hostname").stdout.trim()}`); + assert(bash.executeSync("whoami").stdout.trim() === "agent"); + assert(bash.executeSync("hostname").stdout.trim() === "sandbox"); + + console.log(); +} + +function demoBashTool() { + console.log("=== BashTool Metadata ===\n"); + + const tool = new BashTool(); + console.log(`name: ${tool.name}`); + console.log(`version: ${tool.version}`); + console.log(`short: ${tool.shortDescription}`); + console.log(`schema: ${tool.inputSchema().substring(0, 60)}...`); + assert(tool.name === "bashkit"); + assert(tool.version === getVersion()); + + // Execute through BashTool + const r = tool.executeSync("echo 'hello from BashTool'"); + console.log(`exec: ${r.stdout.trim()}`); + assert(r.exitCode === 0); + + console.log(); +} + +// ============================================================================ + +function assert(condition, msg = "assertion failed") { + if (!condition) throw new Error(msg); +} + +function main() { + console.log(`Bashkit v${getVersion()} — Bash basics examples\n`); + demoBasics(); + demoState(); + demoErrorHandling(); + demoJq(); + demoConfig(); + demoBashTool(); + console.log("All examples passed."); +} + +main(); diff --git a/examples/data_pipeline.mjs b/examples/data_pipeline.mjs new file mode 100644 index 00000000..7d32acf6 --- /dev/null +++ b/examples/data_pipeline.mjs @@ -0,0 +1,196 @@ +#!/usr/bin/env node +/** + * Data processing pipeline using bashkit. + * + * Demonstrates real-world data tasks: CSV processing, JSON transformation, + * log analysis, and report generation — all in a sandboxed virtual filesystem. + * + * Run: + * node examples/data_pipeline.mjs + */ + +import { Bash } from "@everruns/bashkit"; + +function assert(condition, msg = "assertion failed") { + if (!condition) throw new Error(msg); +} + +function demoCsvProcessing() { + console.log("=== CSV Processing ===\n"); + + const bash = new Bash(); + + // Create sample sales data + bash.executeSync(`cat > /tmp/sales.csv << 'EOF' +date,product,quantity,price +2024-01-01,Widget A,10,29.99 +2024-01-01,Widget B,5,49.99 +2024-01-02,Widget A,8,29.99 +2024-01-02,Widget C,12,19.99 +2024-01-03,Widget B,3,49.99 +2024-01-03,Widget A,15,29.99 +2024-01-03,Widget C,7,19.99 +EOF`); + + // Total quantity per product + const r1 = bash.executeSync( + "tail -n +2 /tmp/sales.csv | awk -F, '{sum[$2]+=$3} END {for (p in sum) print p\": \"sum[p]}' | sort" + ); + console.log("Quantity by product:"); + console.log(r1.stdout); + + // Top selling day by quantity + const r2 = bash.executeSync( + "tail -n +2 /tmp/sales.csv | awk -F, '{sum[$1]+=$3} END {for (d in sum) print sum[d]\" \"d}' | sort -rn | head -1" + ); + console.log(`Top day: ${r2.stdout.trim()}`); + + // Revenue calculation + const r3 = bash.executeSync( + "tail -n +2 /tmp/sales.csv | awk -F, '{rev+=$3*$4} END {printf \"$%.2f\\n\", rev}'" + ); + console.log(`Total revenue: ${r3.stdout.trim()}`); + console.log(); +} + +function demoJsonTransformation() { + console.log("=== JSON Transformation ===\n"); + + const bash = new Bash(); + + // Create API-like JSON response + bash.executeSync(`cat > /tmp/api_response.json << 'EOF' +{ + "users": [ + {"id": 1, "name": "Alice", "email": "alice@example.com", "active": true, "role": "admin"}, + {"id": 2, "name": "Bob", "email": "bob@example.com", "active": false, "role": "user"}, + {"id": 3, "name": "Carol", "email": "carol@example.com", "active": true, "role": "user"}, + {"id": 4, "name": "Dave", "email": "dave@example.com", "active": true, "role": "admin"}, + {"id": 5, "name": "Eve", "email": "eve@example.com", "active": false, "role": "user"} + ] +} +EOF`); + + // Active admins + const r1 = bash.executeSync( + 'cat /tmp/api_response.json | jq \'[.users[] | select(.active and .role == "admin")] | length\'' + ); + console.log(`Active admins: ${r1.stdout.trim()}`); + assert(r1.stdout.trim() === "2"); + + // Transform to summary + const r2 = bash.executeSync(` + cat /tmp/api_response.json | jq '{ + total: (.users | length), + active: ([.users[] | select(.active)] | length), + inactive: ([.users[] | select(.active | not)] | length), + admins: [.users[] | select(.role == "admin") | .name], + emails: [.users[] | select(.active) | .email] + }' + `); + const summary = JSON.parse(r2.stdout); + console.log(`Total: ${summary.total}, Active: ${summary.active}, Inactive: ${summary.inactive}`); + console.log(`Admins: ${summary.admins.join(", ")}`); + assert(summary.total === 5); + assert(summary.active === 3); + console.log(); +} + +function demoLogAnalysis() { + console.log("=== Log Analysis ===\n"); + + const bash = new Bash(); + + // Create sample log data + bash.executeSync(`cat > /tmp/access.log << 'EOF' +2024-01-15T10:00:01 GET /api/users 200 45ms +2024-01-15T10:00:02 POST /api/users 201 120ms +2024-01-15T10:00:03 GET /api/users/1 200 30ms +2024-01-15T10:00:04 GET /api/health 200 5ms +2024-01-15T10:00:05 POST /api/login 401 15ms +2024-01-15T10:00:06 GET /api/users 200 42ms +2024-01-15T10:00:07 DELETE /api/users/3 403 10ms +2024-01-15T10:00:08 GET /api/products 500 200ms +2024-01-15T10:00:09 GET /api/users 200 38ms +2024-01-15T10:00:10 POST /api/login 200 95ms +EOF`); + + // Request counts by status code + const r1 = bash.executeSync( + "awk '{print $4}' /tmp/access.log | sort | uniq -c | sort -rn" + ); + console.log("Requests by status:"); + console.log(r1.stdout); + + // Error requests (4xx/5xx) + const r2 = bash.executeSync( + "awk '$4 >= 400 {print $1, $2, $3, $4}' /tmp/access.log" + ); + console.log("Errors:"); + console.log(r2.stdout); + + // Average response time + const r3 = bash.executeSync( + "awk '{gsub(/ms/,\"\",$5); sum+=$5; n++} END {printf \"%.1fms\\n\", sum/n}' /tmp/access.log" + ); + console.log(`Avg response time: ${r3.stdout.trim()}`); + + // Most hit endpoints + const r4 = bash.executeSync( + "awk '{print $3}' /tmp/access.log | sort | uniq -c | sort -rn | head -3" + ); + console.log("Top endpoints:"); + console.log(r4.stdout); +} + +function demoReportGeneration() { + console.log("=== Report Generation ===\n"); + + const bash = new Bash(); + + // Generate a markdown report from data + const r = bash.executeSync(` + # Gather data + echo -e "alice,95\\nbob,82\\ncarol,91\\ndave,78\\neve,88" > /tmp/scores.csv + + # Build report + cat << 'HEADER' +# Student Report + +| Student | Score | Grade | +|---------|-------|-------| +HEADER + + while IFS=, read name score; do + if [ "$score" -ge 90 ]; then grade="A" + elif [ "$score" -ge 80 ]; then grade="B" + elif [ "$score" -ge 70 ]; then grade="C" + else grade="F" + fi + echo "| $name | $score | $grade |" + done < /tmp/scores.csv + + echo "" + AVG=$(awk -F, '{sum+=$2; n++} END {printf "%.1f", sum/n}' /tmp/scores.csv) + TOP=$(sort -t, -k2 -rn /tmp/scores.csv | head -1 | cut -d, -f1) + echo "**Average:** $AVG" + echo "**Top student:** $TOP" + `); + + console.log(r.stdout); + assert(r.stdout.includes("alice")); + assert(r.stdout.includes("Average:")); +} + +// ============================================================================ + +function main() { + console.log("Bashkit — Data Pipeline Examples\n"); + demoCsvProcessing(); + demoJsonTransformation(); + demoLogAnalysis(); + demoReportGeneration(); + console.log("All examples passed."); +} + +main(); diff --git a/examples/langchain_agent.mjs b/examples/langchain_agent.mjs new file mode 100644 index 00000000..209faf2b --- /dev/null +++ b/examples/langchain_agent.mjs @@ -0,0 +1,113 @@ +#!/usr/bin/env node +/** + * Bashkit as a LangChain.js tool with a ReAct agent. + * + * Uses `@langchain/core` and `@langchain/langgraph` to create a ReAct agent + * that can execute bash commands via BashTool. The agent reasons about which + * commands to run and iterates until it has the answer. + * + * Prerequisites: + * npm install @langchain/core @langchain/langgraph @langchain/openai zod + * export OPENAI_API_KEY=sk-... + * + * Run: + * node examples/langchain_agent.mjs + */ + +import { DynamicStructuredTool } from "@langchain/core/tools"; +import { ChatOpenAI } from "@langchain/openai"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { z } from "zod"; +import { BashTool } from "@everruns/bashkit"; + +// ─── Setup ─────────────────────────────────────────────────────────── + +const bashTool = new BashTool({ username: "agent", hostname: "sandbox" }); + +// Wrap BashTool as a LangChain DynamicStructuredTool +const bashLangChainTool = new DynamicStructuredTool({ + name: "bash", + description: [ + bashTool.shortDescription, + "Execute bash commands in a sandboxed virtual filesystem.", + "State persists between calls. Use for file operations, text processing, and scripting.", + ].join(" "), + schema: z.object({ + commands: z + .string() + .describe("Bash commands to execute"), + }), + func: async ({ commands }) => { + const result = bashTool.executeSync(commands); + return JSON.stringify({ + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exitCode, + }); + }, +}); + +// ─── Agent ─────────────────────────────────────────────────────────── + +async function runAgent(userMessage) { + console.log(`\nUser: ${userMessage}\n`); + + const model = new ChatOpenAI({ + model: "gpt-5.4", + modelKwargs: { reasoning_effort: "none" }, + }); + + const agent = createReactAgent({ + llm: model, + tools: [bashLangChainTool], + }); + + const result = await agent.invoke({ + messages: [{ role: "user", content: userMessage }], + }); + + // Print tool calls and final response + for (const msg of result.messages) { + if (msg._getType() === "ai" && msg.tool_calls?.length > 0) { + for (const tc of msg.tool_calls) { + console.log(` [tool] ${tc.name}: ${tc.args.commands}`); + } + } + if (msg._getType() === "tool") { + const parsed = JSON.parse(msg.content); + if (parsed.stdout) console.log(` [out] ${parsed.stdout.trim()}`); + if (parsed.stderr) console.log(` [err] ${parsed.stderr.trim()}`); + } + } + + // Final AI message + const lastAi = result.messages + .filter((m) => m._getType() === "ai" && m.content) + .pop(); + if (lastAi) { + console.log(`\nAssistant: ${lastAi.content}\n`); + } + + return lastAi?.content; +} + +// ─── Main ──────────────────────────────────────────────────────────── + +async function main() { + console.log("Bashkit + LangChain.js ReAct Agent Example"); + console.log("=========================================="); + + await runAgent( + "Create a CSV file at /tmp/employees.csv with 5 employees (name, department, salary). Then find the highest paid employee and their department." + ); +} + +main().catch((err) => { + if (err.message?.includes("API key") || err.message?.includes("OPENAI")) { + console.error( + "Set OPENAI_API_KEY to run this example. See the file header for details." + ); + process.exit(1); + } + throw err; +}); diff --git a/examples/llm_tool.mjs b/examples/llm_tool.mjs new file mode 100644 index 00000000..f5a4636f --- /dev/null +++ b/examples/llm_tool.mjs @@ -0,0 +1,110 @@ +#!/usr/bin/env node +/** + * BashTool as an LLM tool — shows how to wire bashkit into any AI framework. + * + * Demonstrates: + * - Extracting tool definition (name, description, JSON schema) + * - Simulating an LLM tool-call loop (no API key needed) + * - Feeding results back as tool responses + * + * Run: + * node examples/llm_tool.mjs + */ + +import { BashTool } from "@everruns/bashkit"; + +function main() { + const tool = new BashTool({ username: "agent", hostname: "sandbox" }); + + // ─── 1. Tool definition (what you'd send to any LLM) ───────────── + console.log("=== Tool Definition ===\n"); + + const toolDef = { + name: tool.name, + description: tool.shortDescription, + inputSchema: JSON.parse(tool.inputSchema()), + outputSchema: JSON.parse(tool.outputSchema()), + }; + + console.log(`Name: ${toolDef.name}`); + console.log(`Description: ${toolDef.description}`); + console.log(`Input keys: ${Object.keys(toolDef.inputSchema.properties || {}).join(", ")}`); + console.log(); + + // ─── 2. System prompt (token-efficient instructions for the LLM) ── + console.log("=== System Prompt (first 200 chars) ===\n"); + console.log(tool.systemPrompt().substring(0, 200) + "...\n"); + + // ─── 3. Simulate an LLM tool-call loop ──────────────────────────── + console.log("=== Simulated Tool-Call Loop ===\n"); + + // Pretend the LLM decided to call our tool with these commands + const llmToolCalls = [ + 'echo "Setting up project..."', + "mkdir -p /tmp/project/src", + "echo 'console.log(\"hello\")' > /tmp/project/src/index.js", + "cat /tmp/project/src/index.js", + "ls -la /tmp/project/src/", + ]; + + for (const commands of llmToolCalls) { + console.log(`LLM calls: ${commands}`); + const result = tool.executeSync(commands); + + // This is what you'd send back to the LLM as the tool response + const toolResponse = { + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exitCode, + }; + + if (result.exitCode === 0) { + console.log(` → stdout: ${result.stdout.trim() || "(empty)"}`); + } else { + console.log(` → error (${result.exitCode}): ${result.stderr.trim()}`); + } + console.log(); + } + + // ─── 4. Generic tool adapter pattern ─────────────────────────────── + console.log("=== Generic Tool Adapter ===\n"); + + // This function converts BashTool into a format any framework can use + const adapter = createToolAdapter(tool); + console.log(`Adapter name: ${adapter.name}`); + console.log(`Adapter schema: ${JSON.stringify(adapter.schema).substring(0, 80)}...`); + + // Execute through adapter + const adapterResult = adapter.execute({ commands: "echo 'via adapter'" }); + console.log(`Adapter result: ${adapterResult.stdout.trim()}`); + console.log(); + + console.log("All examples passed."); +} + +/** + * Creates a generic tool adapter from BashTool — works with any LLM framework. + * + * Returns an object with: + * - name, description, schema (for tool registration) + * - execute(params) (for tool invocation) + */ +function createToolAdapter(bashTool) { + return { + name: bashTool.name, + description: bashTool.description(), + schema: JSON.parse(bashTool.inputSchema()), + + execute(params) { + const commands = params.commands || params.command || ""; + const result = bashTool.executeSync(commands); + return { + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exitCode, + }; + }, + }; +} + +main(); diff --git a/examples/openai_tool.mjs b/examples/openai_tool.mjs new file mode 100644 index 00000000..33dc5098 --- /dev/null +++ b/examples/openai_tool.mjs @@ -0,0 +1,138 @@ +#!/usr/bin/env node +/** + * Bashkit as an OpenAI function-calling tool. + * + * Uses the official `openai` package to wire BashTool as a function + * the model can call. The model decides when to execute bash commands, + * and results are fed back for the next response. + * + * Prerequisites: + * npm install openai + * export OPENAI_API_KEY=sk-... + * + * Run: + * node examples/openai_tool.mjs + */ + +import OpenAI from "openai"; +import { BashTool } from "@everruns/bashkit"; + +// ─── Setup ─────────────────────────────────────────────────────────── + +const openai = new OpenAI(); +const bashTool = new BashTool({ username: "agent", hostname: "sandbox" }); + +// Define the tool for OpenAI function calling +const tools = [ + { + type: "function", + function: { + name: "bash", + description: bashTool.shortDescription, + parameters: { + type: "object", + properties: { + commands: { + type: "string", + description: + "Bash commands to execute in a sandboxed virtual environment", + }, + }, + required: ["commands"], + additionalProperties: false, + }, + strict: true, + }, + }, +]; + +// ─── Tool execution handler ────────────────────────────────────────── + +function executeTool(name, args) { + if (name !== "bash") { + return JSON.stringify({ error: `Unknown tool: ${name}` }); + } + const result = bashTool.executeSync(args.commands); + return JSON.stringify({ + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exitCode, + }); +} + +// ─── Agent loop ────────────────────────────────────────────────────── + +async function runAgent(userMessage) { + console.log(`\nUser: ${userMessage}\n`); + + const messages = [ + { + role: "system", + content: [ + "You have access to a sandboxed bash interpreter.", + "Use the bash tool to run commands when needed.", + bashTool.systemPrompt(), + ].join("\n"), + }, + { role: "user", content: userMessage }, + ]; + + // Loop until the model produces a final text response + for (let step = 0; step < 10; step++) { + const response = await openai.chat.completions.create({ + model: "gpt-5.4", + reasoning_effort: "none", + messages, + tools, + }); + + const choice = response.choices[0]; + messages.push(choice.message); + + // If no tool calls, we have the final answer + if (!choice.message.tool_calls || choice.message.tool_calls.length === 0) { + console.log(`Assistant: ${choice.message.content}\n`); + return choice.message.content; + } + + // Execute each tool call + for (const toolCall of choice.message.tool_calls) { + const args = JSON.parse(toolCall.function.arguments); + console.log(` [tool] ${toolCall.function.name}: ${args.commands}`); + + const result = executeTool(toolCall.function.name, args); + const parsed = JSON.parse(result); + if (parsed.stdout) console.log(` [out] ${parsed.stdout.trim()}`); + if (parsed.stderr) console.log(` [err] ${parsed.stderr.trim()}`); + + messages.push({ + role: "tool", + tool_call_id: toolCall.id, + content: result, + }); + } + } + + console.log("(max steps reached)"); +} + +// ─── Main ──────────────────────────────────────────────────────────── + +async function main() { + console.log("Bashkit + OpenAI Function Calling Example"); + console.log("========================================="); + + await runAgent( + "Create a file /tmp/greeting.txt with 'Hello World', then count the words in it." + ); +} + +main().catch((err) => { + if (err.message?.includes("API key")) { + console.error( + "Set OPENAI_API_KEY to run this example. See the file header for details." + ); + process.exit(1); + } + throw err; +}); diff --git a/examples/vercel_ai_tool.mjs b/examples/vercel_ai_tool.mjs new file mode 100644 index 00000000..92e6da49 --- /dev/null +++ b/examples/vercel_ai_tool.mjs @@ -0,0 +1,94 @@ +#!/usr/bin/env node +/** + * Bashkit as a Vercel AI SDK tool. + * + * Uses the `ai` package with `@ai-sdk/openai` to wire BashTool as a + * tool that the model can invoke. The AI SDK handles the tool-call loop + * automatically via `generateText` with `maxSteps`. + * + * Prerequisites: + * npm install ai @ai-sdk/openai zod + * export OPENAI_API_KEY=sk-... + * + * Run: + * node examples/vercel_ai_tool.mjs + */ + +import { generateText, tool } from "ai"; +import { openai } from "@ai-sdk/openai"; +import { z } from "zod"; +import { BashTool } from "@everruns/bashkit"; + +// ─── Setup ─────────────────────────────────────────────────────────── + +const bashTool = new BashTool({ username: "agent", hostname: "sandbox" }); + +// Define bashkit as a Vercel AI SDK tool +const bashkitTool = tool({ + description: bashTool.shortDescription, + parameters: z.object({ + commands: z + .string() + .describe("Bash commands to execute in a sandboxed virtual environment"), + }), + execute: async ({ commands }) => { + const result = bashTool.executeSync(commands); + return { + stdout: result.stdout, + stderr: result.stderr, + exit_code: result.exitCode, + }; + }, +}); + +// ─── Agent ─────────────────────────────────────────────────────────── + +async function runAgent(userMessage) { + console.log(`\nUser: ${userMessage}\n`); + + const result = await generateText({ + model: openai("gpt-5.4", { reasoningEffort: "none" }), + system: [ + "You have access to a sandboxed bash interpreter.", + "Use the bash tool to run commands when needed.", + bashTool.systemPrompt(), + ].join("\n"), + prompt: userMessage, + tools: { bash: bashkitTool }, + maxSteps: 10, + onStepFinish: ({ toolResults }) => { + for (const tr of toolResults) { + console.log(` [tool] bash: ${tr.args?.commands ?? "(no args)"}`); + if (tr.result?.stdout) console.log(` [out] ${tr.result.stdout.trim()}`); + if (tr.result?.stderr) console.log(` [err] ${tr.result.stderr.trim()}`); + } + }, + }); + + console.log(`\nAssistant: ${result.text}\n`); + console.log( + `Steps: ${result.steps.length}, Tool calls: ${result.steps.reduce((n, s) => n + (s.toolCalls?.length ?? 0), 0)}` + ); + return result.text; +} + +// ─── Main ──────────────────────────────────────────────────────────── + +async function main() { + console.log("Bashkit + Vercel AI SDK Example"); + console.log("==============================="); + + await runAgent( + "Create a JSON file at /tmp/config.json with a 'name' field set to 'my-app' and 'port' set to 3000. Then read it back and tell me the port number." + ); +} + +main().catch((err) => { + if (err.message?.includes("API key") || err.message?.includes("OPENAI")) { + console.error( + "Set OPENAI_API_KEY to run this example. See the file header for details." + ); + process.exit(1); + } + throw err; +}); diff --git a/specs/008-release-process.md b/specs/008-release-process.md index 0d14edbf..dd2aa57b 100644 --- a/specs/008-release-process.md +++ b/specs/008-release-process.md @@ -150,6 +150,7 @@ Example: - `bashkit` on crates.io (core library) - `bashkit-cli` on crates.io (CLI tool) - `bashkit` on PyPI (Python package, pre-built wheels) +- `@everruns/bashkit` on npm (JavaScript/TypeScript package, native NAPI-RS bindings) ## Publishing Order @@ -160,6 +161,8 @@ Crates must be published in dependency order: Python wheels are published independently (no crates.io dependency). +npm packages are published independently (no crates.io dependency). + The CI workflows handle this automatically on GitHub Release. ## Workflows @@ -185,6 +188,32 @@ The CI workflows handle this automatically on GitHub Release. - **Auth**: PyPI trusted publishing (OIDC, no secrets needed) - **Environment**: `release-python` (must exist in GitHub repo settings) +### publish-js.yml + +- **Trigger**: GitHub Release published (runs in parallel with publish.yml and publish-python.yml) +- **Actions**: Builds native NAPI-RS bindings for all platforms, tests on Node 20/22, publishes to npm +- **File**: `.github/workflows/publish-js.yml` +- **Secret required**: `NPM_TOKEN` (npm access token) +- **Auth**: `id-token: write` for npm provenance (OIDC attestation), same pattern as everruns/sdk + +#### JS native binding matrix + +| OS | Target | Runner | +|----|--------|--------| +| macOS | x86_64-apple-darwin | macos-latest | +| macOS | aarch64-apple-darwin | macos-latest | +| Linux | x86_64-unknown-linux-gnu | ubuntu-latest | +| Linux | aarch64-unknown-linux-gnu | ubuntu-24.04-arm | +| Windows | x86_64-pc-windows-msvc | windows-latest | +| WASM | wasm32-wasip1-threads | ubuntu-latest | + +Node.js versions tested: 20, 22, 24 + +#### JS version sync + +JS package version is synced from `Cargo.toml` workspace version via `build.rs`. +The build script updates `package.json` automatically when the Cargo version changes. + #### Wheel matrix | OS | Architecture | Variant | @@ -217,6 +246,14 @@ Python package version is read dynamically from `Cargo.toml` via maturin - Configure at: https://pypi.org/manage/project/bashkit/settings/publishing/ - Add publisher: GitHub, repo `everruns/bashkit`, workflow `publish-python.yml`, environment `release-python` +**npm Publishing** (same pattern as everruns/sdk): + +- `NPM_TOKEN`: npm access token (GitHub Settings > Secrets > Actions) + - Generate at: https://www.npmjs.com/settings/~/tokens + - Type: Automation +- Provenance enabled via `id-token: write` OIDC permission + `--provenance` flag +- No separate GitHub environment required + ## Example Conversation ``` @@ -260,3 +297,4 @@ Each release includes: - **GitHub Release**: Tag, release notes, source archives - **crates.io**: Published crates for `cargo add bashkit` - **PyPI**: Pre-built wheels for `pip install bashkit` +- **npm**: Native NAPI-RS bindings for `npm install @everruns/bashkit` diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 8d4c0d8a..94f908cc 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -10,6 +10,10 @@ criteria = "safe-to-deploy" [policy.bashkit-cli] criteria = "safe-to-deploy" +[policy.bashkit-js] +audit-as-crates-io = false +criteria = "safe-to-deploy" + [policy.bashkit-python] audit-as-crates-io = false criteria = "safe-to-deploy" @@ -170,6 +174,10 @@ criteria = "safe-to-deploy" version = "1.2.56" criteria = "safe-to-deploy" +[[exemptions.cc]] +version = "1.2.57" +criteria = "safe-to-deploy" + [[exemptions.cesu8]] version = "1.1.0" criteria = "safe-to-deploy" @@ -270,6 +278,10 @@ criteria = "safe-to-deploy" version = "0.15.11" criteria = "safe-to-run" +[[exemptions.convert_case]] +version = "0.11.0" +criteria = "safe-to-deploy" + [[exemptions.core-foundation]] version = "0.9.4" criteria = "safe-to-deploy" @@ -322,6 +334,14 @@ criteria = "safe-to-run" version = "0.1.7" criteria = "safe-to-deploy" +[[exemptions.ctor]] +version = "0.6.3" +criteria = "safe-to-deploy" + +[[exemptions.ctor-proc-macro]] +version = "0.0.7" +criteria = "safe-to-deploy" + [[exemptions.derive-where]] version = "1.6.0" criteria = "safe-to-deploy" @@ -342,6 +362,14 @@ criteria = "safe-to-deploy" version = "0.2.5" criteria = "safe-to-deploy" +[[exemptions.dtor]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.dtor-proc-macro]] +version = "0.0.6" +criteria = "safe-to-deploy" + [[exemptions.dunce]] version = "1.0.5" criteria = "safe-to-deploy" @@ -690,6 +718,10 @@ criteria = "safe-to-deploy" version = "0.2.183" criteria = "safe-to-deploy" +[[exemptions.libloading]] +version = "0.9.0" +criteria = "safe-to-deploy" + [[exemptions.libm]] version = "0.2.16" criteria = "safe-to-deploy" @@ -754,6 +786,30 @@ criteria = "safe-to-deploy" version = "0.33.2" criteria = "safe-to-deploy" +[[exemptions.napi]] +version = "3.8.3" +criteria = "safe-to-deploy" + +[[exemptions.napi-build]] +version = "2.3.1" +criteria = "safe-to-deploy" + +[[exemptions.napi-derive]] +version = "3.5.2" +criteria = "safe-to-deploy" + +[[exemptions.napi-derive-backend]] +version = "5.0.2" +criteria = "safe-to-deploy" + +[[exemptions.napi-sys]] +version = "3.2.1" +criteria = "safe-to-deploy" + +[[exemptions.nohash-hasher]] +version = "0.2.0" +criteria = "safe-to-deploy" + [[exemptions.num-bigint]] version = "0.4.6" criteria = "safe-to-deploy" @@ -1418,6 +1474,10 @@ criteria = "safe-to-deploy" version = "0.1.25" criteria = "safe-to-deploy" +[[exemptions.unicode-segmentation]] +version = "1.12.0" +criteria = "safe-to-deploy" + [[exemptions.unicode-width]] version = "0.2.2" criteria = "safe-to-deploy"