diff --git a/.github/workflows/nodejs-build-call.yml b/.github/workflows/nodejs-build-call.yml new file mode 100644 index 00000000..f0259f88 --- /dev/null +++ b/.github/workflows/nodejs-build-call.yml @@ -0,0 +1,334 @@ +name: Node.js Bindings Build and Test Call + +on: + workflow_call: + workflow_dispatch: + +env: + DEBUG: napi:* + APP_NAME: oci-client + MACOSX_DEPLOYMENT_TARGET: '10.13' + CARGO_INCREMENTAL: '1' + CARGO_TERM_COLOR: always + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # ratchet:actions/setup-node@v6 + with: + node-version: 22 + + - name: Enable Corepack + run: corepack enable + + - name: Install dependencies + working-directory: bindings/nodejs + run: yarn install + + - name: Oxlint + working-directory: bindings/nodejs + run: yarn lint + + build: + name: Build - ${{ matrix.settings.target }} + runs-on: ${{ matrix.settings.host }} + strategy: + fail-fast: false + matrix: + settings: + # All targets use native-tls (platform TLS: Security.framework on macOS, + # OpenSSL on Linux, Schannel on Windows). This avoids aws-lc-sys + # cross-compilation issues and ensures consistent TLS behavior. + - host: macos-latest + target: x86_64-apple-darwin + build: yarn napi build --platform --release --target x86_64-apple-darwin --no-default-features --features native-tls && yarn fix-const-enums + - host: macos-latest + target: aarch64-apple-darwin + build: yarn napi build --platform --release --target aarch64-apple-darwin --no-default-features --features native-tls && yarn fix-const-enums + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + build: yarn napi build --platform --release --target x86_64-unknown-linux-gnu --use-napi-cross --no-default-features --features native-tls-vendored && yarn fix-const-enums + - host: windows-latest + target: x86_64-pc-windows-msvc + build: yarn napi build --platform --release --target x86_64-pc-windows-msvc --no-default-features --features native-tls && yarn fix-const-enums + - host: ubuntu-latest + target: aarch64-unknown-linux-gnu + build: yarn napi build --platform --release --target aarch64-unknown-linux-gnu --use-napi-cross --no-default-features --features native-tls-vendored && yarn fix-const-enums + # Cross-compiled Linux targets use vendored OpenSSL (compiled from source, since cross-toolchain sysroots don't ship it) + - host: ubuntu-latest + target: x86_64-unknown-linux-musl + build: yarn napi build --platform --release --target x86_64-unknown-linux-musl -x --no-default-features --features native-tls-vendored && yarn fix-const-enums + - host: ubuntu-latest + target: aarch64-unknown-linux-musl + build: yarn napi build --platform --release --target aarch64-unknown-linux-musl -x --no-default-features --features native-tls-vendored && yarn fix-const-enums + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # ratchet:actions/setup-node@v6 + with: + node-version: 22 + + - name: Enable Corepack + run: corepack enable + + - name: Install Rust + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # ratchet:dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + + - name: Cache cargo + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # ratchet: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 }}-${{ hashFiles('**/Cargo.lock', 'bindings/nodejs/yarn.lock') }} + restore-keys: | + ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }}- + + - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # ratchet:mlugg/setup-zig@v2 + if: ${{ contains(matrix.settings.target, 'musl') }} + with: + version: 0.14.1 + + - name: Install cargo-zigbuild + uses: taiki-e/install-action@85b24a67ef0c632dfefad70b9d5ce8fddb040754 # ratchet:taiki-e/install-action@v2 + if: ${{ contains(matrix.settings.target, 'musl') }} + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tool: cargo-zigbuild + + - name: Install dependencies + working-directory: bindings/nodejs + run: yarn install + + - name: Build + working-directory: bindings/nodejs + run: ${{ matrix.settings.build }} + shell: bash + + - name: Upload native binary + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # ratchet:actions/upload-artifact@v7 + with: + name: bindings-${{ matrix.settings.target }} + path: bindings/nodejs/${{ env.APP_NAME }}.*.node + if-no-files-found: error + + - name: Upload generated JS/TS bindings + if: matrix.settings.target == 'aarch64-apple-darwin' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # ratchet:actions/upload-artifact@v7 + with: + name: bindings-js + path: | + bindings/nodejs/index.js + bindings/nodejs/index.d.ts + if-no-files-found: error + + test-macOS-windows-binding: + name: Test - ${{ matrix.settings.target }} - node@${{ matrix.node }} + needs: build + runs-on: ${{ matrix.settings.host }} + 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: ['22', '24'] + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # ratchet:actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + architecture: ${{ matrix.settings.architecture }} + + - name: Enable Corepack + run: corepack enable + + - name: Install dependencies + working-directory: bindings/nodejs + run: yarn install + + - name: Download native binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-${{ matrix.settings.target }} + path: bindings/nodejs + + - name: Download generated JS/TS bindings + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-js + path: bindings/nodejs + + - name: Test bindings + working-directory: bindings/nodejs + run: yarn test + env: + # Skip Zot registry tests - no container runtime available on macOS/Windows CI + REQUIRE_PUSH_TESTS: 'false' + + test-linux-x64-binding: + name: Test - x86_64-unknown-linux-gnu - node@${{ matrix.node }} + needs: build + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: ['22', '24'] + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # ratchet:actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + + - name: Enable Corepack + run: corepack enable + + - name: Install dependencies + working-directory: bindings/nodejs + run: yarn install + + - name: Download native binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-x86_64-unknown-linux-gnu + path: bindings/nodejs + + - name: Download generated JS/TS bindings + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-js + path: bindings/nodejs + + - name: Test bindings + working-directory: bindings/nodejs + run: yarn test + env: + REQUIRE_PUSH_TESTS: 'false' + + test-linux-binding: + name: Test - ${{ matrix.target }} - node@${{ matrix.node }} + needs: build + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - aarch64-unknown-linux-gnu + - x86_64-unknown-linux-musl + - aarch64-unknown-linux-musl + node: ['22', '24'] + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # ratchet:actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + + - name: Enable Corepack + run: corepack enable + + - name: Set up QEMU + if: contains(matrix.target, 'aarch64') + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # ratchet:docker/setup-qemu-action@v4 + with: + platforms: arm64 + + - name: Install dependencies + working-directory: bindings/nodejs + run: | + yarn config set --json supportedArchitectures.cpu '["current", "arm64", "x64"]' + yarn config set --json supportedArchitectures.libc '["current", "musl", "gnu"]' + yarn install + + - name: Download native binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-${{ matrix.target }} + path: bindings/nodejs + + - name: Download generated JS/TS bindings + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-js + path: bindings/nodejs + + - name: Test bindings in container + uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 # ratchet:addnab/docker-run-action@v3 + with: + image: node:${{ matrix.node }}${{ contains(matrix.target, 'musl') && '-alpine' || '-slim' }} + options: >- + ${{ contains(matrix.target, 'aarch64') && '--platform linux/arm64' || '' }} + -v ${{ github.workspace }}:/build -w /build/bindings/nodejs + -e REQUIRE_PUSH_TESTS=false + run: corepack enable && yarn test + + test-zot-integration: + name: Integration Tests (Zot Registry) + needs: build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # ratchet:actions/setup-node@v6 + with: + node-version: 22 + + - name: Enable Corepack + run: corepack enable + + - name: Install dependencies + working-directory: bindings/nodejs + run: yarn install + + - name: Download native binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-x86_64-unknown-linux-gnu + path: bindings/nodejs + + - name: Download generated JS/TS bindings + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + name: bindings-js + path: bindings/nodejs + + - name: Run integration tests with Zot registry + working-directory: bindings/nodejs + run: yarn test + env: + # Require push tests - Docker is available on ubuntu-latest + REQUIRE_PUSH_TESTS: 'true' + # Use Docker (pre-installed on GitHub Actions Ubuntu runners) + CONTAINER_RUNTIME: docker diff --git a/.github/workflows/nodejs-build.yml b/.github/workflows/nodejs-build.yml new file mode 100644 index 00000000..98a81e4e --- /dev/null +++ b/.github/workflows/nodejs-build.yml @@ -0,0 +1,28 @@ +name: Node.js Bindings Build and Test + +on: + push: + branches: + - main + paths: + - 'bindings/nodejs/**' + - 'src/**' + - 'Cargo.toml' + - '.github/workflows/nodejs-build-call.yml' + - '.github/workflows/nodejs-build.yml' + pull_request: + paths: + - 'bindings/nodejs/**' + - 'src/**' + - 'Cargo.toml' + - '.github/workflows/nodejs-build-call.yml' + - '.github/workflows/nodejs-build.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-test: + uses: ./.github/workflows/nodejs-build-call.yml + diff --git a/.github/workflows/nodejs-publish-call.yml b/.github/workflows/nodejs-publish-call.yml new file mode 100644 index 00000000..0c902db4 --- /dev/null +++ b/.github/workflows/nodejs-publish-call.yml @@ -0,0 +1,80 @@ +name: Node.js Bindings Publish Call + +on: + workflow_call: + secrets: + NPM_TOKEN: + required: true + workflow_dispatch: + +jobs: + publish: + name: Publish to npm + runs-on: ubuntu-latest + needs: build-and-test-nodejs + permissions: + contents: write + id-token: write + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6 + + - name: Setup node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # ratchet:actions/setup-node@v6 + with: + node-version: 22 + + - name: Verify version consistency + working-directory: bindings/nodejs + run: node scripts/sync-version.js --check + + - name: Enable Corepack + run: corepack enable + + - name: Install dependencies + working-directory: bindings/nodejs + run: yarn install + + - name: Create npm dirs + working-directory: bindings/nodejs + run: yarn napi create-npm-dirs + + - name: Download all artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 + with: + path: bindings/nodejs/artifacts + + - name: Move artifacts + working-directory: bindings/nodejs + run: | + yarn artifacts + cp artifacts/bindings-js/index.js artifacts/bindings-js/index.d.ts . + + - name: List packages + working-directory: bindings/nodejs + run: ls -R ./npm + shell: bash + + - name: Publish + working-directory: bindings/nodejs + run: | + npm config set provenance true + TAG="${GITHUB_REF_NAME:-}" + if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --access public + elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --tag next --access public + else + echo "No release tag detected (GITHUB_REF_NAME='$TAG'), skipping publish" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + # Build and test Node.js bindings FIRST + # This also ensures version sync before any publishing + build-and-test-nodejs: + name: Build & Test Node.js + uses: ./.github/workflows/nodejs-build-call.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7276651e..e86f1885 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,11 +3,31 @@ on: push: tags: - "v*" + jobs: + verify-versions: + name: Verify version consistency + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + # Node.js is pre-installed on GitHub-hosted Ubuntu runners + - name: Check version consistency + working-directory: bindings/nodejs + run: node scripts/sync-version.js --check --tag "$GITHUB_REF_NAME" + publish: name: publish to crates.io runs-on: ubuntu-latest + needs: verify-versions steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: publish oci-distribution to crates.io run: cargo publish --token ${{ secrets.CargoToken }} + + # Publish Node.js packages to npm + publish-nodejs-bindings: + name: Publish to npm + needs: verify-versions + uses: ./.github/workflows/nodejs-publish-call.yml + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index ded0af7d..26c37fbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,17 @@ +[workspace] +members = [".", "bindings/nodejs"] +default-members = ["."] + +[workspace.package] +version = "0.16.1" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/oras-project/rust-oci-client" +homepage = "https://github.com/oras-project/rust-oci-client" + +[workspace.dependencies] +oci-spec = "0.9" + [package] authors = [ "Matt Butcher ", @@ -12,14 +26,14 @@ authors = [ ] description = "An OCI implementation in Rust" documentation = "https://docs.rs/oci-client" -edition = "2021" -homepage = "https://github.com/oras-project/rust-oci-client" +edition.workspace = true +homepage.workspace = true keywords = ["oci", "containers"] -license = "Apache-2.0" +license.workspace = true name = "oci-client" readme = "README.md" -repository = "https://github.com/oras-project/rust-oci-client" -version = "0.16.1" +repository.workspace = true +version.workspace = true [badges] maintenance = { status = "actively-developed" } @@ -41,7 +55,7 @@ http = "1.3" http-auth = { version = "0.1", default-features = false } jsonwebtoken = { version = "10.3", default-features = false } lazy_static = "1.4" -oci-spec = "0.9" +oci-spec.workspace = true olpc-cjson = "0.1" regex = "1.11" reqwest = { version = "0.13", default-features = false, features = [ @@ -73,4 +87,4 @@ tempfile = "3.21" # using the ring feature. Otherwise this fails to compile on Windows testcontainers = "0.27" tokio = { version = "1", features = ["macros", "fs", "rt-multi-thread"] } -tokio-util = { version = "0.7", features = ["compat"] } +tokio-util = { version = "0.7", features = ["compat"] } \ No newline at end of file diff --git a/bindings/nodejs/.editorconfig b/bindings/nodejs/.editorconfig new file mode 100644 index 00000000..0b59bdb4 --- /dev/null +++ b/bindings/nodejs/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + diff --git a/bindings/nodejs/.gitattributes b/bindings/nodejs/.gitattributes new file mode 100644 index 00000000..28f1beb7 --- /dev/null +++ b/bindings/nodejs/.gitattributes @@ -0,0 +1,12 @@ +* text=auto + +*.ts text eol=lf merge=union +*.tsx text eol=lf merge=union +*.rs text eol=lf merge=union +*.js text eol=lf merge=union +*.json text eol=lf merge=union + +# Generated codes +index.js linguist-detectable=false +index.d.ts linguist-detectable=false + diff --git a/bindings/nodejs/.gitignore b/bindings/nodejs/.gitignore new file mode 100644 index 00000000..7ce67ffd --- /dev/null +++ b/bindings/nodejs/.gitignore @@ -0,0 +1,32 @@ +# Generated NAPI bindings +*.node +index.js +index.d.ts + +# Rust build artifacts +target/ + +# Node.js dependencies +node_modules/ + +# npm package directories for distribution +npm/ + +# Build artifacts +*.d.ts.map +*.js.map + +# OS files +.DS_Store +Thumbs.db + +# Yarn +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/sdks +!.yarn/versions + +# Cargo lock (library crate) +Cargo.lock diff --git a/bindings/nodejs/.yarnrc.yml b/bindings/nodejs/.yarnrc.yml new file mode 100644 index 00000000..3186f3f0 --- /dev/null +++ b/bindings/nodejs/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/bindings/nodejs/Cargo.toml b/bindings/nodejs/Cargo.toml new file mode 100644 index 00000000..fe40606d --- /dev/null +++ b/bindings/nodejs/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "oci-client-nodejs-bindings" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +description = "Node.js bindings for rust-oci-client" + +[lib] +crate-type = ["cdylib"] + +[features] +default = ["native-tls"] +native-tls = ["oci-client/native-tls"] +# Vendored OpenSSL for cross-compiled Linux targets where system OpenSSL isn't available +native-tls-vendored = ["oci-client/native-tls", "openssl/vendored"] + +[dependencies] +# NAPI-RS for Node.js bindings +napi = { version = "3", default-features = false, features = ["async", "napi4", "tokio_rt"] } +napi-derive = "3" + +# Core OCI client - path dependency to parent crate +oci-client = { path = "../..", default-features = false } + +# OCI spec types for Platform arch/os enums +oci-spec.workspace = true + +# Async runtime +tokio = { version = "1", features = ["full"] } + +# OpenSSL - optional, only for vendored builds (cross-compiled Linux targets) +openssl = { version = "0.10", optional = true } + +[build-dependencies] +napi-build = "2" + diff --git a/bindings/nodejs/README.md b/bindings/nodejs/README.md new file mode 100644 index 00000000..94c45834 --- /dev/null +++ b/bindings/nodejs/README.md @@ -0,0 +1,212 @@ +# Node.js Bindings for rust-oci-client + +Node.js bindings for the [rust-oci-client](https://github.com/oras-project/rust-oci-client) library, providing high-performance OCI Distribution client functionality. + +**Version**: These bindings are versioned in sync with the parent `oci-client` crate. + +## Features + +- **Pure API Mirror**: Function signatures match the native Rust functions exactly +- **High Performance**: Uses NAPI-RS for zero-copy data transfer where possible +- **Full Auth Support**: Anonymous, Basic (username/password), and Bearer token authentication +- **Complete ClientConfig**: All native configuration options exposed +- **TypeScript Support**: Full type definitions included + +## Installation + +```bash +npm install @oras-project/oci-client +# or +yarn add @oras-project/oci-client +``` + +## Usage + +```typescript +import { OciClient, ClientProtocol, anonymousAuth, basicAuth } from '@oras-project/oci-client'; + +// Create a client with default configuration +const client = new OciClient(); + +// Or with custom configuration +const clientWithConfig = OciClient.withConfig({ + protocol: ClientProtocol.Https, + acceptInvalidCertificates: false, + maxConcurrentDownload: 8, + maxConcurrentUpload: 8, +}); + +// Create a client targeting a specific platform (for multi-arch images) +const armClient = OciClient.withConfig({ + platform: { + os: 'linux', + architecture: 'arm64', + variant: 'v8' // optional + } +}); + +// Pull an image +const imageData = await client.pull( + 'ghcr.io/example/image:latest', + anonymousAuth(), + ['application/vnd.oci.image.layer.v1.tar+gzip'] +); + +console.log(`Pulled ${imageData.layers.length} layers`); +console.log(`Digest: ${imageData.digest}`); + +// Push an image +const response = await client.push( + 'registry.example.com/myimage:v1', + layers, + config, + basicAuth('username', 'password'), + null // Let the client generate the manifest +); + +console.log(`Manifest URL: ${response.manifestUrl}`); + +// Pull image manifest +const { manifest, digest } = await client.pullImageManifest( + 'ghcr.io/example/image:latest', + anonymousAuth() +); + +// Push a manifest list (multi-platform image) +const manifestUrl = await client.pushManifestList( + 'registry.example.com/myimage:v1', + basicAuth('username', 'password'), + imageIndex +); + +// Pull referrers (OCI 1.1) +const referrers = await client.pullReferrers( + 'ghcr.io/example/image@sha256:abc123...', + 'application/vnd.example.sbom' +); +``` + +## API Reference + +### Client + +#### `new OciClient()` +Create a client with default configuration. + +#### `OciClient.withConfig(config: ClientConfig)` +Create a client with custom configuration. + +### Platform Selection + +For multi-platform images (Image Index/Manifest List), you can specify the target platform: + +```typescript +const client = OciClient.withConfig({ + platform: { + os: 'linux', // Required: linux, windows, darwin, etc. + architecture: 'arm64', // Required: amd64, arm64, arm, etc. + variant: 'v8' // Optional: v7, v8, etc. for ARM + } +}); +``` + +When pulling an image that references an Image Index, the client will automatically select the manifest matching the specified platform. + +### Authentication + +#### `anonymousAuth()` +Create anonymous authentication. + +#### `basicAuth(username: string, password: string)` +Create HTTP Basic authentication. + +#### `bearerAuth(token: string)` +Create Bearer token authentication. + +### Main Functions + +#### `pull(image, auth, acceptedMediaTypes)` +Pull an image from the registry. Returns `ImageData` with layers as Buffers. + +#### `push(imageRef, layers, config, auth, manifest?)` +Push an image to the registry. Returns `PushResponse`. + +#### `pullImageManifest(image, auth)` +Pull an image manifest. Returns `{ manifest, digest }`. If a multi-platform Image Index is encountered, automatically selects the platform-specific manifest. + +#### `pullManifest(image, auth)` +Pull a manifest (either image or image index) from the registry. Returns `{ manifest, digest }`. + +#### `pullManifestRaw(image, auth, acceptedMediaTypes)` +Pull a manifest as raw bytes. Returns a `Buffer`. + +#### `pushManifest(image, manifest)` +Push a manifest (image or image index) to the registry. Returns the manifest URL. + +#### `pushManifestList(reference, auth, manifest)` +Push a manifest list (image index). Returns manifest URL. + +#### `pullReferrers(image, artifactType?)` +Pull referrers for an artifact (OCI 1.1 Referrers API). Returns `ImageIndex`. + +#### `pullBlob(image, digest)` +Pull a blob from the registry. Returns a `Buffer`. + +#### `pushBlob(image, data, digest)` +Push a blob to the registry. Returns the blob digest. + +#### `blobExists(image, digest)` +Check if a blob exists in the registry. Returns `boolean`. + +#### `mountBlob(target, source, digest)` +Mount a blob from one repository to another (cross-repository blob mounting). + +#### `listTags(image, auth, n?, last?)` +List tags for a repository. Supports pagination via `n` (page size) and `last` (last tag from previous page). Returns `string[]`. + +#### `fetchManifestDigest(image, auth)` +Fetch a manifest's digest without downloading the full manifest content. Returns the digest string. + +#### `storeAuth(registry, auth)` +Pre-authenticate with a registry. Useful for storing credentials before performing multiple operations. + +### Types + +See the TypeScript definitions for complete type information. + +## Building from Source + +```bash +# Install dependencies +yarn install + +# Build native module (release) +yarn build + +# Build debug version +yarn build:debug + +# Run tests +yarn test + +# Lint +yarn lint +``` + +## Supported Platforms + +- Windows x64 (MSVC) +- macOS x64 (Intel) +- macOS ARM64 (Apple Silicon) +- Linux x64 (glibc) +- Linux ARM64 (glibc) +- Linux x64 (musl/Alpine) +- Linux ARM64 (musl/Alpine) + +## Contributing + +See the [CONTRIBUTING.md](../../CONTRIBUTING.md) file in the repository root for contribution guidelines. + +## License + +Apache-2.0 diff --git a/bindings/nodejs/__test__/bindings.spec.ts b/bindings/nodejs/__test__/bindings.spec.ts new file mode 100644 index 00000000..776e680b --- /dev/null +++ b/bindings/nodejs/__test__/bindings.spec.ts @@ -0,0 +1,920 @@ +/** + * Tests for the Node.js bindings of rust-oci-client + * + * These tests validate that the NAPI bindings work correctly and produce + * the same results as the native Rust implementation. + * + * Pull tests use a mock OCI registry server (no Docker Hub, no rate limits). + * Push tests use a Zot registry container (requires Docker or Podman). + */ + +import test, { registerCompletionHandler } from 'ava' +registerCompletionHandler(() => process.exit(0)) +import * as crypto from 'crypto' +import { + OciClient, + anonymousAuth, + basicAuth, + bearerAuth, + RegistryAuthType, + ClientProtocol, + CertificateEncoding, + ManifestType, + // Media type constants + IMAGE_LAYER_MEDIA_TYPE, + IMAGE_LAYER_GZIP_MEDIA_TYPE, + IMAGE_CONFIG_MEDIA_TYPE, + IMAGE_DOCKER_LAYER_GZIP_MEDIA_TYPE, + OCI_IMAGE_MEDIA_TYPE, + OCI_IMAGE_INDEX_MEDIA_TYPE, + // Annotation constants + ORG_OPENCONTAINERS_IMAGE_TITLE, + ORG_OPENCONTAINERS_IMAGE_CREATED, + ORG_OPENCONTAINERS_IMAGE_REF_NAME, + // Types + type RegistryAuth, + type ClientConfig, + type ImageLayer, + type Config, + type ImageData, + type ImageManifest, + type ImageIndex, + type Descriptor, + type PlatformSpec, + type Manifest, +} from '../index.js' +import { + MockRegistry, + MANIFEST_DIGEST, CONFIG_DIGEST, BLOB_DIGEST, + AMD64_MANIFEST_DIGEST, ARM64_MANIFEST_DIGEST, IMAGE_INDEX_DIGEST, +} from './mock-registry.js' +import { ZotRegistry, shouldSkipZotTests } from './zot-registry.js' + +// ============================================================================= +// Authentication Tests +// ============================================================================= + +test('anonymousAuth - should create anonymous auth object', (t) => { + const auth: RegistryAuth = anonymousAuth() + t.truthy(auth) + t.is(auth.authType, RegistryAuthType.Anonymous) + t.is(auth.username, undefined) + t.is(auth.password, undefined) + t.is(auth.token, undefined) +}) + +test('basicAuth - should create basic auth object with credentials', (t) => { + const auth: RegistryAuth = basicAuth('testuser', 'testpass') + t.truthy(auth) + t.is(auth.authType, RegistryAuthType.Basic) + t.is(auth.username, 'testuser') + t.is(auth.password, 'testpass') + t.is(auth.token, undefined) +}) + +test('basicAuth - should handle empty credentials', (t) => { + const auth: RegistryAuth = basicAuth('', '') + t.is(auth.authType, RegistryAuthType.Basic) + t.is(auth.username, '') + t.is(auth.password, '') +}) + +test('bearerAuth - should create bearer auth object with token', (t) => { + const auth: RegistryAuth = bearerAuth('my-secret-token') + t.truthy(auth) + t.is(auth.authType, RegistryAuthType.Bearer) + t.is(auth.token, 'my-secret-token') + t.is(auth.username, undefined) + t.is(auth.password, undefined) +}) + +// ============================================================================= +// OciClient Tests +// ============================================================================= + +test('OciClient - should create client with default configuration', (t) => { + const client = new OciClient() + t.truthy(client) + t.true(client instanceof OciClient) +}) + +test('OciClient.withConfig - should create client with custom protocol (Http)', (t) => { + const client = OciClient.withConfig({ + protocol: ClientProtocol.Http, + }) + t.truthy(client) + t.true(client instanceof OciClient) +}) + +test('OciClient.withConfig - should create client with custom protocol (Https)', (t) => { + const client = OciClient.withConfig({ + protocol: ClientProtocol.Https, + }) + t.truthy(client) +}) + +test('OciClient.withConfig - should create client with HttpsExcept protocol', (t) => { + const client = OciClient.withConfig({ + protocol: ClientProtocol.HttpsExcept, + httpsExceptRegistries: ['localhost:5000', '127.0.0.1:5000'], + }) + t.truthy(client) +}) + +test('OciClient.withConfig - should create client with accept invalid certificates', (t) => { + const client = OciClient.withConfig({ + acceptInvalidCertificates: true, + }) + t.truthy(client) +}) + +test('OciClient.withConfig - should create client with concurrency settings', (t) => { + const client = OciClient.withConfig({ + maxConcurrentUpload: 4, + maxConcurrentDownload: 8, + }) + t.truthy(client) +}) + +test('OciClient.withConfig - should create client with timeout settings', (t) => { + const client = OciClient.withConfig({ + readTimeoutMs: 30000, + connectTimeoutMs: 10000, + }) + t.truthy(client) +}) + +test('OciClient.withConfig - should create client with proxy settings', (t) => { + const client = OciClient.withConfig({ + httpsProxy: 'http://proxy.example.com:8080', + httpProxy: 'http://proxy.example.com:8080', + noProxy: 'localhost,127.0.0.1', + }) + t.truthy(client) +}) + +test('OciClient.withConfig - should create client with all settings combined', (t) => { + const config: ClientConfig = { + protocol: ClientProtocol.Https, + acceptInvalidCertificates: false, + useMonolithicPush: false, + maxConcurrentUpload: 8, + maxConcurrentDownload: 16, + defaultTokenExpirationSecs: 300, + readTimeoutMs: 60000, + connectTimeoutMs: 15000, + } + const client = OciClient.withConfig(config) + t.truthy(client) +}) + +test('OciClient.withConfig - should create client with custom certificates', (t) => { + const dummyCertPem = Buffer.from(`-----BEGIN CERTIFICATE----- +MIIBkTCB+wIJAKHBfpB+dEzxMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBnVu +dXNlZDAeFw0yNDAxMDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMBExDzANBgNVBAMM +BnVudXNlZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC5mG0lCDMvz/n9WQH7dlfN +zQkFqW9sMSqvX9qPxN1LmQE7fv/9k1p7q8VDqy6RhDz1f9nNqvHXX1XqHqXJKJBp +AgMBAAGjUzBRMB0GA1UdDgQWBBQJ7W7lXPqXtdJ9gJ8cKo9E7VtZyjAfBgNVHSME +GDAWgBQJ7W7lXPqXtdJ9gJ8cKo9E7VtZyjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBCwUAA0EAG9NxyMEKYE8fzhzLgDz7MQMP3XL7kDqPqRnvJQGLNJQrvSj5 +5M/hDp3eXrWzLgJPqPcC1H3B9cCNqLz8NB/32g== +-----END CERTIFICATE-----`) + + const config: ClientConfig = { + protocol: ClientProtocol.Https, + extraRootCertificates: [ + { + encoding: CertificateEncoding.Pem, + data: dummyCertPem, + }, + ], + } + const client = OciClient.withConfig(config) + t.truthy(client) +}) + +// ============================================================================= +// Type Structure Tests +// ============================================================================= + +test('ImageLayer - should accept valid ImageLayer structure', (t) => { + const layer: ImageLayer = { + data: Buffer.from('test data'), + mediaType: IMAGE_LAYER_GZIP_MEDIA_TYPE, + annotations: { [ORG_OPENCONTAINERS_IMAGE_TITLE]: 'layer.tar.gz' }, + } + t.true(layer.data instanceof Buffer) + t.is(layer.mediaType, IMAGE_LAYER_GZIP_MEDIA_TYPE) + t.truthy(layer.annotations) +}) + +test('ImageLayer - should accept ImageLayer without annotations', (t) => { + const layer: ImageLayer = { + data: Buffer.from('test'), + mediaType: IMAGE_LAYER_MEDIA_TYPE, + } + t.is(layer.annotations, undefined) +}) + +test('Config - should accept valid Config structure', (t) => { + const config: Config = { + data: Buffer.from('{}'), + mediaType: IMAGE_CONFIG_MEDIA_TYPE, + annotations: { 'custom.annotation': 'value' }, + } + t.true(config.data instanceof Buffer) + t.is(config.mediaType, IMAGE_CONFIG_MEDIA_TYPE) +}) + +test('Descriptor - should accept valid Descriptor structure', (t) => { + const descriptor: Descriptor = { + mediaType: IMAGE_LAYER_GZIP_MEDIA_TYPE, + digest: 'sha256:abc123def456', + size: 1024, + urls: ['https://example.com/blob'], + annotations: { [ORG_OPENCONTAINERS_IMAGE_TITLE]: 'test' }, + } + t.truthy(descriptor.mediaType) + t.truthy(descriptor.digest) + t.is(descriptor.size, 1024) +}) + +test('Descriptor - should accept minimal Descriptor', (t) => { + const descriptor: Descriptor = { + mediaType: IMAGE_LAYER_MEDIA_TYPE, + digest: 'sha256:abc', + size: 0, + } + t.is(descriptor.urls, undefined) + t.is(descriptor.annotations, undefined) +}) + +test('PlatformSpec - should accept full PlatformSpec', (t) => { + const platform: PlatformSpec = { + architecture: 'amd64', + os: 'linux', + osVersion: '5.4.0', + osFeatures: ['sse4'], + variant: 'v8', + features: ['avx'], + } + t.is(platform.architecture, 'amd64') + t.is(platform.os, 'linux') +}) + +test('PlatformSpec - should accept minimal PlatformSpec', (t) => { + const platform: PlatformSpec = { + architecture: 'arm64', + os: 'darwin', + } + t.is(platform.osVersion, undefined) +}) + +test('ImageManifest - should accept valid ImageManifest structure', (t) => { + const manifest: ImageManifest = { + schemaVersion: 2, + mediaType: OCI_IMAGE_MEDIA_TYPE, + config: { + mediaType: IMAGE_CONFIG_MEDIA_TYPE, + digest: 'sha256:config123', + size: 512, + }, + layers: [ + { + mediaType: IMAGE_LAYER_GZIP_MEDIA_TYPE, + digest: 'sha256:layer123', + size: 2048, + }, + ], + artifactType: 'application/vnd.example.artifact', + annotations: { [ORG_OPENCONTAINERS_IMAGE_CREATED]: '2024-01-01T00:00:00Z' }, + } + t.is(manifest.schemaVersion, 2) + t.is(manifest.config.digest, 'sha256:config123') + t.is(manifest.layers.length, 1) +}) + +test('ImageIndex - should accept valid ImageIndex structure', (t) => { + const index: ImageIndex = { + schemaVersion: 2, + mediaType: OCI_IMAGE_INDEX_MEDIA_TYPE, + manifests: [ + { + mediaType: OCI_IMAGE_MEDIA_TYPE, + digest: 'sha256:manifest123', + size: 1024, + platform: { + architecture: 'amd64', + os: 'linux', + }, + }, + { + mediaType: OCI_IMAGE_MEDIA_TYPE, + digest: 'sha256:manifest456', + size: 1024, + platform: { + architecture: 'arm64', + os: 'linux', + }, + }, + ], + annotations: { [ORG_OPENCONTAINERS_IMAGE_REF_NAME]: 'latest' }, + } + t.is(index.schemaVersion, 2) + t.is(index.manifests.length, 2) +}) + +// ============================================================================= +// Registry Operations using Mock Server +// ============================================================================= + +let mockRegistry: MockRegistry +let mockClient: OciClient +let MOCK_REGISTRY: string + +test.before(async () => { + mockRegistry = new MockRegistry() + await mockRegistry.start() + MOCK_REGISTRY = mockRegistry.address + mockClient = OciClient.withConfig({ protocol: ClientProtocol.Http }) + console.log(`🧪 Mock registry started on ${MOCK_REGISTRY}`) +}) + +test.after(async () => { + await mockRegistry.stop() +}) + +test.serial('pullManifest - should pull manifest from mock registry', async (t) => { + const result = await mockClient.pullManifest( + `${MOCK_REGISTRY}/test:latest`, + anonymousAuth() + ) + + t.truthy(result) + t.truthy(result.manifest) + t.is(result.digest, MANIFEST_DIGEST) + t.is(result.manifest.manifestType, ManifestType.Image) + t.is(result.manifest.image!.schemaVersion, 2) +}) + +test.serial('pullManifest - should pull manifest by digest', async (t) => { + const result = await mockClient.pullManifest( + `${MOCK_REGISTRY}/test@${MANIFEST_DIGEST}`, + anonymousAuth() + ) + + t.is(result.digest, MANIFEST_DIGEST) +}) + +test.serial('fetchManifestDigest - should fetch manifest digest without downloading full manifest', async (t) => { + const digest = await mockClient.fetchManifestDigest( + `${MOCK_REGISTRY}/test:latest`, + anonymousAuth() + ) + + t.is(digest, MANIFEST_DIGEST) +}) + +test.serial('listTags - should list tags from mock registry', async (t) => { + const tags = await mockClient.listTags( + `${MOCK_REGISTRY}/test`, + anonymousAuth(), + 10, + undefined + ) + + t.true(tags.includes('latest')) + t.true(tags.includes('v1')) +}) + +test.serial('pullBlob - should pull config blob by digest', async (t) => { + const configData = await mockClient.pullBlob( + `${MOCK_REGISTRY}/test:latest`, + CONFIG_DIGEST + ) + + t.true(configData instanceof Buffer) + t.true(configData.length > 0) + + const configJson = JSON.parse(configData.toString('utf-8')) + t.is(configJson.architecture, 'amd64') + t.is(configJson.os, 'linux') +}) + +test.serial('pullBlob - should pull layer blob by digest', async (t) => { + const layerData = await mockClient.pullBlob( + `${MOCK_REGISTRY}/test:latest`, + BLOB_DIGEST + ) + + t.true(layerData instanceof Buffer) + t.true(layerData.length > 0) +}) + +test.serial('blobExists - should return true for existing blob', async (t) => { + const exists = await mockClient.blobExists( + `${MOCK_REGISTRY}/test:latest`, + CONFIG_DIGEST + ) + t.true(exists) +}) + +test.serial('blobExists - should return false for non-existing blob', async (t) => { + const fakeDigest = 'sha256:0000000000000000000000000000000000000000000000000000000000000000' + const exists = await mockClient.blobExists( + `${MOCK_REGISTRY}/test:latest`, + fakeDigest + ) + t.false(exists) +}) + +test.serial('pull - should pull full image with layers', async (t) => { + const imageData: ImageData = await mockClient.pull( + `${MOCK_REGISTRY}/test@${MANIFEST_DIGEST}`, + anonymousAuth(), + [IMAGE_DOCKER_LAYER_GZIP_MEDIA_TYPE] + ) + + t.truthy(imageData) + t.truthy(imageData.layers) + t.is(imageData.layers.length, 1) + t.truthy(imageData.config) + t.true(imageData.config.data instanceof Buffer) + t.truthy(imageData.digest) + + t.truthy(imageData.manifest) + t.is(imageData.manifest!.schemaVersion, 2) + t.is(imageData.manifest!.layers!.length, imageData.layers.length) + + t.true(imageData.layers[0].data.length > 0) +}) + +test.serial('Error Handling - should throw error for invalid image reference', async (t) => { + await t.throwsAsync( + mockClient.pullManifest('invalid:::reference', anonymousAuth()) + ) +}) + +test.serial('Error Handling - should throw error for non-existent blob', async (t) => { + await t.throwsAsync( + mockClient.pullBlob( + `${MOCK_REGISTRY}/test:latest`, + 'sha256:nonexistent0000000000000000000000000000000000000000000000000000' + ) + ) +}) + +test.serial('storeAuth - should store auth for later use', async (t) => { + const client = new OciClient() + + await t.notThrowsAsync(client.storeAuth('docker.io', anonymousAuth())) + await t.notThrowsAsync(client.storeAuth('ghcr.io', basicAuth('user', 'token'))) +}) + +// ============================================================================= +// Multi-Platform Image Tests (Mock Registry) +// ============================================================================= + +test.serial('multiarch pullManifest - should return Image Index with platform entries', async (t) => { + const result = await mockClient.pullManifest( + `${MOCK_REGISTRY}/test-multiarch:latest`, + anonymousAuth() + ) + + t.truthy(result) + t.is(result.digest, IMAGE_INDEX_DIGEST) + t.is(result.manifest.manifestType, ManifestType.ImageIndex) + t.truthy(result.manifest.imageIndex) + + const index = result.manifest.imageIndex! + t.is(index.manifests.length, 2) + + const amd64Entry = index.manifests.find((m) => m.platform?.architecture === 'amd64') + t.truthy(amd64Entry) + t.is(amd64Entry!.platform!.os, 'linux') + t.is(amd64Entry!.digest, AMD64_MANIFEST_DIGEST) + + const arm64Entry = index.manifests.find((m) => m.platform?.architecture === 'arm64') + t.truthy(arm64Entry) + t.is(arm64Entry!.platform!.os, 'linux') + t.is(arm64Entry!.digest, ARM64_MANIFEST_DIGEST) +}) + +test.serial('multiarch pullImageManifest - should select linux/amd64 with platform filter', async (t) => { + const client = OciClient.withConfig({ + protocol: ClientProtocol.Http, + platform: { os: 'linux', architecture: 'amd64' }, + }) + + const result = await client.pullImageManifest( + `${MOCK_REGISTRY}/test-multiarch:latest`, + anonymousAuth() + ) + + t.truthy(result) + t.is(result.digest, AMD64_MANIFEST_DIGEST) + t.is(result.manifest.schemaVersion, 2) +}) + +test.serial('multiarch pullImageManifest - should select linux/arm64 with platform filter', async (t) => { + const client = OciClient.withConfig({ + protocol: ClientProtocol.Http, + platform: { os: 'linux', architecture: 'arm64' }, + }) + + const result = await client.pullImageManifest( + `${MOCK_REGISTRY}/test-multiarch:latest`, + anonymousAuth() + ) + + t.truthy(result) + t.is(result.digest, ARM64_MANIFEST_DIGEST) + t.is(result.manifest.schemaVersion, 2) +}) + +test.serial('multiarch pullImageManifest - should fail for non-existent platform', async (t) => { + const client = OciClient.withConfig({ + protocol: ClientProtocol.Http, + platform: { os: 'freebsd', architecture: 's390x' }, + }) + + const err = await t.throwsAsync( + client.pullImageManifest( + `${MOCK_REGISTRY}/test-multiarch:latest`, + anonymousAuth() + ) + ) + + t.truthy(err) + t.true(err!.message.includes('no entry found')) +}) + +test.serial('multiarch pull - should pull full image with platform filter (linux/amd64)', async (t) => { + const client = OciClient.withConfig({ + protocol: ClientProtocol.Http, + platform: { os: 'linux', architecture: 'amd64' }, + }) + + const imageData: ImageData = await client.pull( + `${MOCK_REGISTRY}/test-multiarch:latest`, + anonymousAuth(), + [IMAGE_LAYER_MEDIA_TYPE] + ) + + t.truthy(imageData) + t.truthy(imageData.layers) + t.is(imageData.layers.length, 1) + t.truthy(imageData.config) + + const configJson = JSON.parse(imageData.config.data.toString('utf-8')) + t.is(configJson.architecture, 'amd64') + t.is(configJson.os, 'linux') +}) + +// ============================================================================= +// Push Tests with Zot Registry +// ============================================================================= + +const skipZot = shouldSkipZotTests() + +const zot = new ZotRegistry() +let ZOT_REGISTRY: string +let ZOT_REPO: string +let zotClient: OciClient + +if (!skipZot) { + test.before(async () => { + await zot.start() + ZOT_REGISTRY = zot.address + ZOT_REPO = zot.repo('test-oci-client') + zotClient = zot.createClient() + }) + + test.after(async () => { + await zot.stop() + }) +} + +const zotTest = skipZot ? test.skip : test.serial + +zotTest('pushBlob - should push a blob to the registry', async (t) => { + const testData = Buffer.from('Hello, OCI World!') + const hash = crypto.createHash('sha256').update(testData).digest('hex') + const digest = `sha256:${hash}` + + const result = await zotClient.pushBlob(`${ZOT_REPO}:test`, testData, digest) + + t.truthy(result) + t.true(result.includes(digest)) +}) + +zotTest('pushBlob - should verify pushed blob exists', async (t) => { + const testData = Buffer.from('Test blob data for existence check') + const hash = crypto.createHash('sha256').update(testData).digest('hex') + const digest = `sha256:${hash}` + + await zotClient.pushBlob(`${ZOT_REPO}:test`, testData, digest) + + const exists = await zotClient.blobExists(`${ZOT_REPO}:test`, digest) + t.true(exists) +}) + +zotTest('pushBlob - should pull back the pushed blob with same content', async (t) => { + const originalData = Buffer.from('Roundtrip test data: ' + Date.now()) + const hash = crypto.createHash('sha256').update(originalData).digest('hex') + const digest = `sha256:${hash}` + + await zotClient.pushBlob(`${ZOT_REPO}:test`, originalData, digest) + + const pulledData = await zotClient.pullBlob(`${ZOT_REPO}:test`, digest) + + t.true(pulledData instanceof Buffer) + t.is(pulledData.toString(), originalData.toString()) + t.is(pulledData.length, originalData.length) +}) + +zotTest('pushManifest - should push a simple OCI image manifest', async (t) => { + const configData = Buffer.from(JSON.stringify({ + architecture: 'amd64', + os: 'linux', + config: {}, + rootfs: { type: 'layers', diff_ids: [] }, + })) + const configHash = crypto.createHash('sha256').update(configData).digest('hex') + const configDigest = `sha256:${configHash}` + + const layerData = Buffer.from('test layer content') + const layerHash = crypto.createHash('sha256').update(layerData).digest('hex') + const layerDigest = `sha256:${layerHash}` + + await zotClient.pushBlob(`${ZOT_REPO}:v1`, configData, configDigest) + await zotClient.pushBlob(`${ZOT_REPO}:v1`, layerData, layerDigest) + + const imageManifest: ImageManifest = { + schemaVersion: 2, + mediaType: OCI_IMAGE_MEDIA_TYPE, + config: { + mediaType: IMAGE_CONFIG_MEDIA_TYPE, + digest: configDigest, + size: configData.length, + }, + layers: [ + { + mediaType: IMAGE_LAYER_MEDIA_TYPE, + digest: layerDigest, + size: layerData.length, + }, + ], + } + + const manifest: Manifest = { + manifestType: ManifestType.Image, + image: imageManifest, + } + + const manifestUrl = await zotClient.pushManifest(`${ZOT_REPO}:v1`, manifest) + + t.truthy(manifestUrl) + t.true(manifestUrl.includes(ZOT_REGISTRY)) +}) + +zotTest('push - should push a complete image using the push() method', async (t) => { + const tag = `full-${Date.now()}` + + const layer1Data = Buffer.from('Layer 1 content: ' + Date.now()) + const layer2Data = Buffer.from('Layer 2 content: ' + Date.now()) + + const layers: ImageLayer[] = [ + { + data: layer1Data, + mediaType: IMAGE_LAYER_MEDIA_TYPE, + annotations: { [ORG_OPENCONTAINERS_IMAGE_TITLE]: 'layer1.tar' }, + }, + { + data: layer2Data, + mediaType: IMAGE_LAYER_MEDIA_TYPE, + annotations: { [ORG_OPENCONTAINERS_IMAGE_TITLE]: 'layer2.tar' }, + }, + ] + + const configJson = JSON.stringify({ + architecture: 'amd64', + os: 'linux', + config: { + Env: ['PATH=/usr/local/bin:/usr/bin:/bin'], + Cmd: ['/bin/sh'], + }, + rootfs: { + type: 'layers', + diff_ids: [], + }, + history: [ + { created: new Date().toISOString(), created_by: 'test' }, + ], + }) + + const config: Config = { + data: Buffer.from(configJson), + mediaType: IMAGE_CONFIG_MEDIA_TYPE, + } + + const response = await zotClient.push( + `${ZOT_REPO}:${tag}`, + layers, + config, + anonymousAuth(), + undefined + ) + + t.truthy(response) + t.truthy(response.configUrl) + t.truthy(response.manifestUrl) + t.true(response.manifestUrl.includes(ZOT_REGISTRY)) + + const pulledResult = await zotClient.pullManifest( + `${ZOT_REPO}:${tag}`, + anonymousAuth() + ) + + t.is(pulledResult.manifest.manifestType, ManifestType.Image) + const pulledManifest = pulledResult.manifest.image! + t.is(pulledManifest.layers!.length, 2) +}) + +// ============================================================================= +// Multi-Platform Tests with Zot Registry +// ============================================================================= + +zotTest('pushManifestList + pullManifest - should round-trip platform fields', async (t) => { + const ZOT_MULTIARCH = zot.repo('test-multiarch-roundtrip') + + // Push a minimal image for amd64 + const amd64Config = Buffer.from(JSON.stringify({ + architecture: 'amd64', os: 'linux', + config: {}, rootfs: { type: 'layers', diff_ids: [] }, + })) + const amd64ConfigDigest = `sha256:${crypto.createHash('sha256').update(amd64Config).digest('hex')}` + const amd64Layer = Buffer.from('amd64-zot-layer-' + Date.now()) + const amd64LayerDigest = `sha256:${crypto.createHash('sha256').update(amd64Layer).digest('hex')}` + + await zotClient.pushBlob(`${ZOT_MULTIARCH}:amd64`, amd64Config, amd64ConfigDigest) + await zotClient.pushBlob(`${ZOT_MULTIARCH}:amd64`, amd64Layer, amd64LayerDigest) + + const amd64Manifest: ImageManifest = { + schemaVersion: 2, + mediaType: OCI_IMAGE_MEDIA_TYPE, + config: { mediaType: IMAGE_CONFIG_MEDIA_TYPE, digest: amd64ConfigDigest, size: amd64Config.length }, + layers: [{ mediaType: IMAGE_LAYER_MEDIA_TYPE, digest: amd64LayerDigest, size: amd64Layer.length }], + } + await zotClient.pushManifest(`${ZOT_MULTIARCH}:amd64`, { manifestType: ManifestType.Image, image: amd64Manifest }) + + // Push a minimal image for arm64 + const arm64Config = Buffer.from(JSON.stringify({ + architecture: 'arm64', os: 'linux', + config: {}, rootfs: { type: 'layers', diff_ids: [] }, + })) + const arm64ConfigDigest = `sha256:${crypto.createHash('sha256').update(arm64Config).digest('hex')}` + const arm64Layer = Buffer.from('arm64-zot-layer-' + Date.now()) + const arm64LayerDigest = `sha256:${crypto.createHash('sha256').update(arm64Layer).digest('hex')}` + + await zotClient.pushBlob(`${ZOT_MULTIARCH}:arm64`, arm64Config, arm64ConfigDigest) + await zotClient.pushBlob(`${ZOT_MULTIARCH}:arm64`, arm64Layer, arm64LayerDigest) + + const arm64Manifest: ImageManifest = { + schemaVersion: 2, + mediaType: OCI_IMAGE_MEDIA_TYPE, + config: { mediaType: IMAGE_CONFIG_MEDIA_TYPE, digest: arm64ConfigDigest, size: arm64Config.length }, + layers: [{ mediaType: IMAGE_LAYER_MEDIA_TYPE, digest: arm64LayerDigest, size: arm64Layer.length }], + } + await zotClient.pushManifest(`${ZOT_MULTIARCH}:arm64`, { manifestType: ManifestType.Image, image: arm64Manifest }) + + // Get exact digests and sizes for the index entries + const amd64Digest = await zotClient.fetchManifestDigest(`${ZOT_MULTIARCH}:amd64`, anonymousAuth()) + const arm64Digest = await zotClient.fetchManifestDigest(`${ZOT_MULTIARCH}:arm64`, anonymousAuth()) + const amd64Raw = await zotClient.pullManifestRaw(`${ZOT_MULTIARCH}:amd64`, anonymousAuth(), [OCI_IMAGE_MEDIA_TYPE]) + const arm64Raw = await zotClient.pullManifestRaw(`${ZOT_MULTIARCH}:arm64`, anonymousAuth(), [OCI_IMAGE_MEDIA_TYPE]) + + // Push the Image Index + const index: ImageIndex = { + schemaVersion: 2, + mediaType: OCI_IMAGE_INDEX_MEDIA_TYPE, + manifests: [ + { + mediaType: OCI_IMAGE_MEDIA_TYPE, + digest: amd64Digest, + size: amd64Raw.length, + platform: { architecture: 'amd64', os: 'linux' }, + }, + { + mediaType: OCI_IMAGE_MEDIA_TYPE, + digest: arm64Digest, + size: arm64Raw.length, + platform: { architecture: 'arm64', os: 'linux' }, + }, + ], + } + await zotClient.pushManifestList(`${ZOT_MULTIARCH}:multiarch`, anonymousAuth(), index) + + // Pull back and verify platform fields survived the round-trip + const pulled = await zotClient.pullManifest(`${ZOT_MULTIARCH}:multiarch`, anonymousAuth()) + t.is(pulled.manifest.manifestType, ManifestType.ImageIndex) + const pulledIndex = pulled.manifest.imageIndex! + t.is(pulledIndex.manifests.length, 2) + + const pulledAmd64 = pulledIndex.manifests.find((m) => m.digest === amd64Digest) + t.truthy(pulledAmd64) + t.is(pulledAmd64!.platform!.os, 'linux') + t.is(pulledAmd64!.platform!.architecture, 'amd64') + + const pulledArm64 = pulledIndex.manifests.find((m) => m.digest === arm64Digest) + t.truthy(pulledArm64) + t.is(pulledArm64!.platform!.os, 'linux') + t.is(pulledArm64!.platform!.architecture, 'arm64') +}) + +zotTest('pullImageManifest - should select correct platform from multi-arch image via Zot', async (t) => { + const ZOT_MULTIARCH = zot.repo('test-multiarch-filter') + + // Push amd64 image + const amd64Config = Buffer.from(JSON.stringify({ + architecture: 'amd64', os: 'linux', + config: {}, rootfs: { type: 'layers', diff_ids: [] }, + })) + const amd64ConfigDigest = `sha256:${crypto.createHash('sha256').update(amd64Config).digest('hex')}` + const amd64Layer = Buffer.from('amd64-filter-layer-' + Date.now()) + const amd64LayerDigest = `sha256:${crypto.createHash('sha256').update(amd64Layer).digest('hex')}` + + await zotClient.pushBlob(`${ZOT_MULTIARCH}:amd64`, amd64Config, amd64ConfigDigest) + await zotClient.pushBlob(`${ZOT_MULTIARCH}:amd64`, amd64Layer, amd64LayerDigest) + await zotClient.pushManifest(`${ZOT_MULTIARCH}:amd64`, { + manifestType: ManifestType.Image, + image: { + schemaVersion: 2, + mediaType: OCI_IMAGE_MEDIA_TYPE, + config: { mediaType: IMAGE_CONFIG_MEDIA_TYPE, digest: amd64ConfigDigest, size: amd64Config.length }, + layers: [{ mediaType: IMAGE_LAYER_MEDIA_TYPE, digest: amd64LayerDigest, size: amd64Layer.length }], + }, + }) + + // Push arm64 image + const arm64Config = Buffer.from(JSON.stringify({ + architecture: 'arm64', os: 'linux', + config: {}, rootfs: { type: 'layers', diff_ids: [] }, + })) + const arm64ConfigDigest = `sha256:${crypto.createHash('sha256').update(arm64Config).digest('hex')}` + const arm64Layer = Buffer.from('arm64-filter-layer-' + Date.now()) + const arm64LayerDigest = `sha256:${crypto.createHash('sha256').update(arm64Layer).digest('hex')}` + + await zotClient.pushBlob(`${ZOT_MULTIARCH}:arm64`, arm64Config, arm64ConfigDigest) + await zotClient.pushBlob(`${ZOT_MULTIARCH}:arm64`, arm64Layer, arm64LayerDigest) + await zotClient.pushManifest(`${ZOT_MULTIARCH}:arm64`, { + manifestType: ManifestType.Image, + image: { + schemaVersion: 2, + mediaType: OCI_IMAGE_MEDIA_TYPE, + config: { mediaType: IMAGE_CONFIG_MEDIA_TYPE, digest: arm64ConfigDigest, size: arm64Config.length }, + layers: [{ mediaType: IMAGE_LAYER_MEDIA_TYPE, digest: arm64LayerDigest, size: arm64Layer.length }], + }, + }) + + // Get exact digests and sizes + const amd64Digest = await zotClient.fetchManifestDigest(`${ZOT_MULTIARCH}:amd64`, anonymousAuth()) + const arm64Digest = await zotClient.fetchManifestDigest(`${ZOT_MULTIARCH}:arm64`, anonymousAuth()) + const amd64Raw = await zotClient.pullManifestRaw(`${ZOT_MULTIARCH}:amd64`, anonymousAuth(), [OCI_IMAGE_MEDIA_TYPE]) + const arm64Raw = await zotClient.pullManifestRaw(`${ZOT_MULTIARCH}:arm64`, anonymousAuth(), [OCI_IMAGE_MEDIA_TYPE]) + + // Push Image Index + await zotClient.pushManifestList(`${ZOT_MULTIARCH}:multiarch`, anonymousAuth(), { + schemaVersion: 2, + mediaType: OCI_IMAGE_INDEX_MEDIA_TYPE, + manifests: [ + { + mediaType: OCI_IMAGE_MEDIA_TYPE, + digest: amd64Digest, + size: amd64Raw.length, + platform: { architecture: 'amd64', os: 'linux' }, + }, + { + mediaType: OCI_IMAGE_MEDIA_TYPE, + digest: arm64Digest, + size: arm64Raw.length, + platform: { architecture: 'arm64', os: 'linux' }, + }, + ], + }) + + // Pull with platform filter -- this should select the amd64 manifest + const platformClient = OciClient.withConfig({ + protocol: ClientProtocol.Http, + platform: { os: 'linux', architecture: 'amd64' }, + }) + + const result = await platformClient.pullImageManifest( + `${ZOT_MULTIARCH}:multiarch`, + anonymousAuth() + ) + + t.is(result.digest, amd64Digest) + t.is(result.manifest.schemaVersion, 2) +}) + diff --git a/bindings/nodejs/__test__/mock-registry.ts b/bindings/nodejs/__test__/mock-registry.ts new file mode 100644 index 00000000..5e13d5a3 --- /dev/null +++ b/bindings/nodejs/__test__/mock-registry.ts @@ -0,0 +1,341 @@ +/** + * Mock OCI Registry Server + * + * Simple HTTP server using the same fixtures as the native Rust tests. + * Modeled after BadServer from tests/digest_validation.rs + * + * Also serves a multi-arch Image Index on /v2/test-multiarch/... for + * testing platform resolution. + */ + +import * as http from 'http' +import * as fs from 'fs' +import * as path from 'path' +import * as crypto from 'crypto' +import { fileURLToPath } from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +function sha256digest(buf: Buffer): string { + return `sha256:${crypto.createHash('sha256').update(buf).digest('hex')}` +} + +// --------------------------------------------------------------------------- +// Single-platform fixtures (from native Rust tests) +// --------------------------------------------------------------------------- + +const FIXTURES_DIR = path.join(__dirname, '..', '..', '..', 'tests', 'fixtures') +const MANIFEST = fs.readFileSync(path.join(FIXTURES_DIR, 'manifest.json')) +const CONFIG = fs.readFileSync(path.join(FIXTURES_DIR, 'config.json')) +const BLOB = fs.readFileSync(path.join(FIXTURES_DIR, 'blob.tar.gz')) + +export const MANIFEST_DIGEST = sha256digest(MANIFEST) +export const CONFIG_DIGEST = sha256digest(CONFIG) +export const BLOB_DIGEST = sha256digest(BLOB) + +// --------------------------------------------------------------------------- +// Multi-arch fixtures: two platform-specific images (linux/amd64, linux/arm64) +// --------------------------------------------------------------------------- + +const AMD64_CONFIG = Buffer.from(JSON.stringify({ + architecture: 'amd64', + os: 'linux', + config: {}, + rootfs: { type: 'layers', diff_ids: [] }, +})) +const AMD64_LAYER = Buffer.from('amd64-layer-content') +const AMD64_CONFIG_DIGEST = sha256digest(AMD64_CONFIG) +const AMD64_LAYER_DIGEST = sha256digest(AMD64_LAYER) + +const AMD64_MANIFEST_BUF = Buffer.from(JSON.stringify({ + schemaVersion: 2, + mediaType: 'application/vnd.oci.image.manifest.v1+json', + config: { + mediaType: 'application/vnd.oci.image.config.v1+json', + digest: AMD64_CONFIG_DIGEST, + size: AMD64_CONFIG.length, + }, + layers: [{ + mediaType: 'application/vnd.oci.image.layer.v1.tar', + digest: AMD64_LAYER_DIGEST, + size: AMD64_LAYER.length, + }], +})) + +const ARM64_CONFIG = Buffer.from(JSON.stringify({ + architecture: 'arm64', + os: 'linux', + config: {}, + rootfs: { type: 'layers', diff_ids: [] }, +})) +const ARM64_LAYER = Buffer.from('arm64-layer-content') +const ARM64_CONFIG_DIGEST = sha256digest(ARM64_CONFIG) +const ARM64_LAYER_DIGEST = sha256digest(ARM64_LAYER) + +const ARM64_MANIFEST_BUF = Buffer.from(JSON.stringify({ + schemaVersion: 2, + mediaType: 'application/vnd.oci.image.manifest.v1+json', + config: { + mediaType: 'application/vnd.oci.image.config.v1+json', + digest: ARM64_CONFIG_DIGEST, + size: ARM64_CONFIG.length, + }, + layers: [{ + mediaType: 'application/vnd.oci.image.layer.v1.tar', + digest: ARM64_LAYER_DIGEST, + size: ARM64_LAYER.length, + }], +})) + +export const AMD64_MANIFEST_DIGEST = sha256digest(AMD64_MANIFEST_BUF) +export const ARM64_MANIFEST_DIGEST = sha256digest(ARM64_MANIFEST_BUF) + +const IMAGE_INDEX_BUF = Buffer.from(JSON.stringify({ + schemaVersion: 2, + mediaType: 'application/vnd.oci.image.index.v1+json', + manifests: [ + { + mediaType: 'application/vnd.oci.image.manifest.v1+json', + digest: AMD64_MANIFEST_DIGEST, + size: AMD64_MANIFEST_BUF.length, + platform: { architecture: 'amd64', os: 'linux' }, + }, + { + mediaType: 'application/vnd.oci.image.manifest.v1+json', + digest: ARM64_MANIFEST_DIGEST, + size: ARM64_MANIFEST_BUF.length, + platform: { architecture: 'arm64', os: 'linux' }, + }, + ], +})) +export const IMAGE_INDEX_DIGEST = sha256digest(IMAGE_INDEX_BUF) + +const MULTIARCH_BLOBS = new Map([ + [AMD64_CONFIG_DIGEST, AMD64_CONFIG], + [AMD64_LAYER_DIGEST, AMD64_LAYER], + [ARM64_CONFIG_DIGEST, ARM64_CONFIG], + [ARM64_LAYER_DIGEST, ARM64_LAYER], +]) + +const MULTIARCH_MANIFESTS = new Map([ + [AMD64_MANIFEST_DIGEST, AMD64_MANIFEST_BUF], + [ARM64_MANIFEST_DIGEST, ARM64_MANIFEST_BUF], +]) + +// --------------------------------------------------------------------------- + +const DIGEST_HEADER = 'Docker-Content-Digest' + +export interface MockConfig { + badManifest?: boolean + badConfig?: boolean + badBlob?: boolean +} + +export class MockRegistry { + private server: http.Server | null = null + private _port = 0 + + constructor(private config: MockConfig = {}) {} + + get port() { + return this._port + } + get address() { + return `127.0.0.1:${this._port}` + } + + async start(): Promise { + this.server = http.createServer((req, res) => { + const url = req.url || '' + + // /v2/ - API version check + if (url === '/v2/' || url === '/v2') { + res.writeHead(200) + res.end('{}') + return + } + + // Route multi-arch repo separately + if (url.startsWith('/v2/test-multiarch/')) { + this.handleMultiarch(req, res, url) + return + } + + // /v2/{name}/manifests/{ref} + if (url.includes('/manifests/')) { + this.serveManifest(req, res) + return + } + + // /v2/{name}/blobs/{digest} + if (url.includes('/blobs/')) { + this.serveBlob(req, res, url) + return + } + + // /v2/{name}/tags/list + if (url.includes('/tags/list')) { + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ name: 'test', tags: ['latest', 'v1', 'v2'] })) + return + } + + res.writeHead(404) + res.end() + }) + + return new Promise((resolve) => { + this.server!.listen(0, '127.0.0.1', () => { + const addr = this.server!.address() as { port: number } + this._port = addr.port + resolve() + }) + }) + } + + // --- Single-platform routes (original /v2/test/...) --- + + private serveManifest(req: http.IncomingMessage, res: http.ServerResponse) { + const digest = this.config.badManifest ? 'sha256:bad' : MANIFEST_DIGEST + + if (req.method === 'HEAD') { + res.writeHead(200, { + 'Content-Type': 'application/vnd.docker.distribution.manifest.v2+json', + [DIGEST_HEADER]: digest, + 'Content-Length': MANIFEST.length.toString(), + }) + res.end() + } else { + res.writeHead(200, { + 'Content-Type': 'application/vnd.docker.distribution.manifest.v2+json', + [DIGEST_HEADER]: digest, + }) + res.end(MANIFEST) + } + } + + private serveBlob(req: http.IncomingMessage, res: http.ServerResponse, url: string) { + const requestedDigest = decodeURIComponent(url.split('/blobs/')[1]) + + let content: Buffer + let digest: string + + if (requestedDigest === CONFIG_DIGEST) { + content = CONFIG + digest = this.config.badConfig ? 'sha256:bad' : CONFIG_DIGEST + } else if (requestedDigest === BLOB_DIGEST) { + content = BLOB + digest = this.config.badBlob ? 'sha256:bad' : BLOB_DIGEST + } else { + res.writeHead(404) + res.end() + return + } + + if (req.method === 'HEAD') { + res.writeHead(200, { + [DIGEST_HEADER]: digest, + 'Content-Length': content.length.toString(), + }) + res.end() + } else { + res.writeHead(200, { [DIGEST_HEADER]: digest }) + res.end(content) + } + } + + // --- Multi-arch routes (/v2/test-multiarch/...) --- + + private handleMultiarch(req: http.IncomingMessage, res: http.ServerResponse, url: string) { + if (url.includes('/manifests/')) { + this.serveMultiarchManifest(req, res, url) + } else if (url.includes('/blobs/')) { + this.serveMultiarchBlob(req, res, url) + } else if (url.includes('/tags/list')) { + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ name: 'test-multiarch', tags: ['latest'] })) + } else { + res.writeHead(404) + res.end() + } + } + + private serveMultiarchManifest(req: http.IncomingMessage, res: http.ServerResponse, url: string) { + const ref = decodeURIComponent(url.split('/manifests/')[1]) + + if (ref === 'latest' || ref === IMAGE_INDEX_DIGEST) { + const contentType = 'application/vnd.oci.image.index.v1+json' + if (req.method === 'HEAD') { + res.writeHead(200, { + 'Content-Type': contentType, + [DIGEST_HEADER]: IMAGE_INDEX_DIGEST, + 'Content-Length': IMAGE_INDEX_BUF.length.toString(), + }) + res.end() + } else { + res.writeHead(200, { + 'Content-Type': contentType, + [DIGEST_HEADER]: IMAGE_INDEX_DIGEST, + }) + res.end(IMAGE_INDEX_BUF) + } + return + } + + const manifest = MULTIARCH_MANIFESTS.get(ref) + if (manifest) { + const contentType = 'application/vnd.oci.image.manifest.v1+json' + if (req.method === 'HEAD') { + res.writeHead(200, { + 'Content-Type': contentType, + [DIGEST_HEADER]: ref, + 'Content-Length': manifest.length.toString(), + }) + res.end() + } else { + res.writeHead(200, { + 'Content-Type': contentType, + [DIGEST_HEADER]: ref, + }) + res.end(manifest) + } + return + } + + res.writeHead(404) + res.end() + } + + private serveMultiarchBlob(req: http.IncomingMessage, res: http.ServerResponse, url: string) { + const requestedDigest = decodeURIComponent(url.split('/blobs/')[1]) + const content = MULTIARCH_BLOBS.get(requestedDigest) + + if (!content) { + res.writeHead(404) + res.end() + return + } + + if (req.method === 'HEAD') { + res.writeHead(200, { + [DIGEST_HEADER]: requestedDigest, + 'Content-Length': content.length.toString(), + }) + res.end() + } else { + res.writeHead(200, { [DIGEST_HEADER]: requestedDigest }) + res.end(content) + } + } + + async stop(): Promise { + if (this.server) { + return new Promise((resolve) => { + this.server!.close(() => resolve()) + this.server!.closeAllConnections() + }) + } + } +} + diff --git a/bindings/nodejs/__test__/package.json b/bindings/nodejs/__test__/package.json new file mode 100644 index 00000000..52a3a1e8 --- /dev/null +++ b/bindings/nodejs/__test__/package.json @@ -0,0 +1,4 @@ +{ + "type": "module" +} + diff --git a/bindings/nodejs/__test__/tsconfig.json b/bindings/nodejs/__test__/tsconfig.json new file mode 100644 index 00000000..ee7bed19 --- /dev/null +++ b/bindings/nodejs/__test__/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "outDir": "lib", + "rootDir": "." + }, + "include": ["*.ts"], + "exclude": ["lib"] +} + diff --git a/bindings/nodejs/__test__/zot-registry.ts b/bindings/nodejs/__test__/zot-registry.ts new file mode 100644 index 00000000..a5a7c15b --- /dev/null +++ b/bindings/nodejs/__test__/zot-registry.ts @@ -0,0 +1,270 @@ +/** + * Zot Registry Utilities + * + * Shared utilities for managing a Zot registry container in tests. + * The registry is persistent across test runs to avoid issues. + */ + +import { execSync } from 'child_process' +import * as net from 'net' +import { OciClient, ClientProtocol } from '../index.js' + +export const CONTAINER_NAME_PREFIX = 'oci-client-test-registry' +export const ZOT_IMAGE = 'ghcr.io/project-zot/zot-minimal:latest' + +// Port range for registry instances (use high ports to avoid conflicts) +const PORT_RANGE_START = 15000 +const PORT_RANGE_SIZE = 10000 + +// Counter to ensure unique container names within the same process +let instanceCounter = 0 + +/** + * Get a random starting port to avoid race conditions when multiple test files start at once + */ +function getRandomStartPort(): number { + return PORT_RANGE_START + Math.floor(Math.random() * PORT_RANGE_SIZE) +} + +export type ContainerRuntime = 'podman' | 'docker' + +/** + * Check if a port is available + */ +export async function isPortAvailable(port: number): Promise { + return new Promise((resolve) => { + const server = net.createServer() + server.once('error', () => resolve(false)) + server.once('listening', () => { + server.close() + resolve(true) + }) + server.listen(port) + }) +} + +/** + * Find an available port starting from a given port + */ +export async function findAvailablePort(startPort: number): Promise { + for (let port = startPort; port < startPort + 100; port++) { + if (await isPortAvailable(port)) { + return port + } + } + throw new Error(`No available port found in range ${startPort}-${startPort + 100}`) +} + +/** + * Wait for the registry to be ready + */ +export async function waitForRegistry(port: number, maxAttempts = 30): Promise { + for (let i = 0; i < maxAttempts; i++) { + try { + const response = await fetch(`http://localhost:${port}/v2/`) + if (response.ok) { + console.log(`✅ Zot registry is ready on port ${port}`) + return + } + } catch { + // Registry not ready yet + } + await new Promise((resolve) => setTimeout(resolve, 1000)) + } + throw new Error(`Registry failed to start on port ${port}`) +} + +// Cache the runtime detection result +let cachedRuntime: ContainerRuntime | null | undefined = undefined + +/** + * Detect available container runtime (cached) + */ +export function detectContainerRuntime(): ContainerRuntime | null { + if (cachedRuntime !== undefined) { + return cachedRuntime + } + + const envRuntime = process.env.CONTAINER_RUNTIME?.toLowerCase() + if (envRuntime === 'podman' || envRuntime === 'docker') { + try { + execSync(`${envRuntime} --version`, { stdio: 'ignore' }) + console.log(`🐳 Using container runtime from CONTAINER_RUNTIME env: ${envRuntime}`) + cachedRuntime = envRuntime + return envRuntime + } catch { + console.warn(`⚠️ CONTAINER_RUNTIME=${envRuntime} specified but not available`) + } + } + + try { + execSync('podman --version', { stdio: 'ignore' }) + console.log('🐳 Auto-detected container runtime: podman') + cachedRuntime = 'podman' + return 'podman' + } catch { + // podman not available + } + + try { + execSync('docker --version', { stdio: 'ignore' }) + console.log('🐳 Auto-detected container runtime: docker') + cachedRuntime = 'docker' + return 'docker' + } catch { + // docker not available + } + + cachedRuntime = null + return null +} + +/** + * Zot Registry Manager + * + * Manages the lifecycle of a Zot registry container for testing. + * The registry persists between test runs - it's only started if not already running. + * + * To stop the registry manually, run: + * podman rm -f oci-client-test-registry + * or + * docker rm -f oci-client-test-registry + */ +export class ZotRegistry { + private _port: number = 0 + private _runtime: ContainerRuntime | null = null + private _started: boolean = false + private _containerName: string + + constructor() { + // Generate unique container name to avoid conflicts when test files run in parallel + this._containerName = `${CONTAINER_NAME_PREFIX}-${process.pid}-${++instanceCounter}` + } + + get port(): number { + return this._port + } + get address(): string { + return `localhost:${this._port}` + } + get runtime(): ContainerRuntime | null { + return this._runtime + } + get isStarted(): boolean { + return this._started + } + get containerName(): string { + return this._containerName + } + + /** + * Start the Zot registry container. + * Each instance starts its own container with a unique name. + */ + async start(): Promise { + this._runtime = detectContainerRuntime() + + if (!this._runtime) { + throw new Error('No container runtime (podman/docker) available') + } + + console.log(`🚀 Starting Zot registry using ${this._runtime}...`) + + // Use random starting port to avoid race conditions when parallel test files start + const startPort = getRandomStartPort() + this._port = await findAvailablePort(startPort) + + // Clean up any existing stopped container with same name + try { + execSync(`${this._runtime} rm -f ${this._containerName} 2>/dev/null`, { stdio: 'ignore' }) + } catch { + // Ignore errors + } + + // Start Zot registry + execSync(`${this._runtime} run -d --name ${this._containerName} -p ${this._port}:5000 ${ZOT_IMAGE}`, { + stdio: 'inherit', + }) + + await waitForRegistry(this._port) + this._started = true + } + + /** + * Stop the Zot registry container. + * + * By default, the container is stopped after tests complete. + * Set KEEP_ZOT_REGISTRY=1 to keep it running for faster subsequent test runs. + */ + async stop(): Promise { + if (!this._started) return + + const shouldKeep = process.env.KEEP_ZOT_REGISTRY === '1' || process.env.KEEP_ZOT_REGISTRY === 'true' + + if (shouldKeep) { + console.log('♻️ Keeping Zot registry running (KEEP_ZOT_REGISTRY=1)') + return + } + + if (this._runtime) { + console.log('🧹 Stopping Zot registry container...') + try { + execSync(`${this._runtime} rm -f ${this._containerName}`, { stdio: 'ignore' }) + } catch { + // Ignore errors + } + } + + this._started = false + } + + /** + * Create an OCI client configured for this registry + */ + createClient(): OciClient { + return OciClient.withConfig({ + protocol: ClientProtocol.Http, + }) + } + + /** + * Get a repository reference for this registry + */ + repo(name: string): string { + return `${this.address}/${name}` + } +} + +// Cache the skip check result +let cachedSkipResult: boolean | undefined = undefined + +/** + * Check if Zot tests should be skipped (cached) + */ +export function shouldSkipZotTests(): boolean { + if (cachedSkipResult !== undefined) { + return cachedSkipResult + } + + // Check if tests are explicitly disabled (e.g., on Windows/macOS CI where Linux containers aren't available) + if (process.env.REQUIRE_PUSH_TESTS === 'false') { + console.log('⏭️ Skipping Zot tests: REQUIRE_PUSH_TESTS is false') + cachedSkipResult = true + return true + } + + const runtime = detectContainerRuntime() + if (!runtime) { + const requireTests = process.env.REQUIRE_PUSH_TESTS === 'true' + if (requireTests) { + throw new Error('REQUIRE_PUSH_TESTS is set but no container runtime (podman/docker) is available') + } + console.log('⏭️ Skipping Zot tests: No container runtime available') + cachedSkipResult = true + return true + } + + cachedSkipResult = false + return false +} + diff --git a/bindings/nodejs/build.rs b/bindings/nodejs/build.rs new file mode 100644 index 00000000..0f1b0100 --- /dev/null +++ b/bindings/nodejs/build.rs @@ -0,0 +1,3 @@ +fn main() { + napi_build::setup(); +} diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json new file mode 100644 index 00000000..05229997 --- /dev/null +++ b/bindings/nodejs/package.json @@ -0,0 +1,81 @@ +{ + "name": "@oras-project/oci-client", + "version": "0.16.1", + "description": "Node.js bindings for rust-oci-client - OCI Distribution client", + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.d.ts", + "index.js" + ], + "napi": { + "binaryName": "oci-client", + "targets": [ + "x86_64-pc-windows-msvc", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "aarch64-unknown-linux-gnu", + "aarch64-apple-darwin", + "aarch64-unknown-linux-musl" + ] + }, + "engines": { + "node": ">= 22" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "scripts": { + "artifacts": "napi artifacts", + "build": "yarn sync-version && napi build --platform --release && yarn fix-const-enums", + "build:debug": "yarn sync-version && napi build --platform && yarn fix-const-enums", + "fix-const-enums": "node scripts/fix-const-enums.js", + "lint": "oxlint .", + "prepublishOnly": "napi prepublish -t npm --no-gh-release", + "test": "ava", + "version": "napi version", + "sync-version": "node scripts/sync-version.js" + }, + "devDependencies": { + "@napi-rs/cli": "^3.2.0", + "@oxc-node/core": "^0.0.35", + "@types/node": "^20.0.0", + "ava": "^6.4.1", + "oxlint": "^1.14.0", + "typescript": "^5.9.2" + }, + "ava": { + "extensions": { + "ts": "module" + }, + "timeout": "2m", + "workerThreads": false, + "environmentVariables": { + "OXC_TSCONFIG_PATH": "./__test__/tsconfig.json" + }, + "nodeArguments": [ + "--import", + "@oxc-node/core/register" + ] + }, + "keywords": [ + "oci", + "registry", + "container", + "docker", + "distribution", + "napi", + "rust", + "bindings" + ], + "author": "dfestal@redhat.com", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/oras-project/rust-oci-client.git", + "directory": "bindings/nodejs" + }, + "packageManager": "yarn@4.12.0" +} diff --git a/bindings/nodejs/scripts/fix-const-enums.js b/bindings/nodejs/scripts/fix-const-enums.js new file mode 100644 index 00000000..2e30fe85 --- /dev/null +++ b/bindings/nodejs/scripts/fix-const-enums.js @@ -0,0 +1,18 @@ +const fs = require("fs"); + +const file = "index.d.ts"; + +if (!fs.existsSync(file)) { + console.log("No index.d.ts to fix (not yet generated)"); + process.exit(0); +} + +const content = fs.readFileSync(file, "utf8"); +const fixed = content.replace(/export declare const enum/g, "export enum"); + +if (content !== fixed) { + fs.writeFileSync(file, fixed); + console.log("Fixed const enum declarations in", file); +} else { + console.log("No const enum declarations to fix in", file); +} diff --git a/bindings/nodejs/scripts/sync-version.js b/bindings/nodejs/scripts/sync-version.js new file mode 100644 index 00000000..2357afa1 --- /dev/null +++ b/bindings/nodejs/scripts/sync-version.js @@ -0,0 +1,38 @@ +const fs = require("fs"); +const args = process.argv.slice(2); +const checkOnly = args.includes("--check"); +const tagIdx = args.indexOf("--tag"); +const tag = tagIdx !== -1 ? args[tagIdx + 1]?.replace(/^v/, "") : null; + +const cargo = fs + .readFileSync("../../Cargo.toml", "utf8") + .match(/^version = "(.+)"/m)?.[1]; + +if (!cargo) { + console.error("Could not read version from Cargo.toml"); + process.exit(1); +} + +const pkgPath = "package.json"; +const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); + +console.log(`Cargo.toml: ${cargo} | package.json: ${pkg.version}${tag ? ` | tag: ${tag}` : ""}`); + +let failed = false; +if (tag && cargo !== tag) { + console.error(`::error::Cargo.toml version (${cargo}) does not match git tag (${tag})`); + failed = true; +} +if (pkg.version !== cargo) { + if (checkOnly) { + console.error(`::error::package.json version (${pkg.version}) does not match Cargo.toml (${cargo})`); + failed = true; + } else { + pkg.version = cargo; + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); + console.log("Synced package.json version to", cargo); + } +} else { + console.log("Versions in sync:", cargo); +} +if (failed) process.exit(1); diff --git a/bindings/nodejs/src/lib.rs b/bindings/nodejs/src/lib.rs new file mode 100644 index 00000000..3224a342 --- /dev/null +++ b/bindings/nodejs/src/lib.rs @@ -0,0 +1,1181 @@ +//! Node.js NAPI bindings for rust-oci-client +//! +//! This module provides a pure, precise JavaScript API mirror of the native oci-client library. +//! All function signatures match the native Rust functions exactly. + +use napi::bindgen_prelude::*; +use napi_derive::napi; + +use oci_client::client::{ + Certificate as NativeCertificate, CertificateEncoding as NativeCertificateEncoding, + ClientConfig as NativeClientConfig, ClientProtocol as NativeClientProtocol, + Config as NativeConfig, ImageData as NativeImageData, ImageLayer as NativeImageLayer, + PushResponse as NativePushResponse, +}; +use oci_client::manifest::{ + ImageIndexEntry, OciDescriptor, OciImageIndex, OciImageManifest, OciManifest, Platform, +}; +use oci_client::secrets::RegistryAuth as NativeRegistryAuth; +use oci_client::{Client, Reference}; +use oci_spec::image::{Arch, Os}; + +use std::collections::BTreeMap; +use std::str::FromStr; +use std::time::Duration; + +fn format_error_chain(err: &dyn std::error::Error) -> String { + let mut msg = err.to_string(); + let mut current = err.source(); + while let Some(source) = current { + msg.push_str(": "); + msg.push_str(&source.to_string()); + current = source.source(); + } + msg +} + +fn oci_error(context: &str, err: oci_client::errors::OciDistributionError) -> Error { + Error::from_reason(format!("{}: {}", context, format_error_chain(&err))) +} + +// ============================================================================ +// Authentication Types - Mirror RegistryAuth exactly +// ============================================================================ + +/// Authentication method for registry access. +/// Mirrors the native RegistryAuth enum exactly. +#[napi(string_enum)] +pub enum RegistryAuthType { + /// Access the registry anonymously + Anonymous, + /// Access the registry using HTTP Basic authentication + Basic, + /// Access the registry using Bearer token authentication + Bearer, +} + +/// Registry authentication configuration. +/// Use `auth_type` to specify the authentication method. +#[napi(object)] +pub struct RegistryAuth { + /// The type of authentication to use + pub auth_type: RegistryAuthType, + /// Username for Basic auth (required when auth_type is Basic) + pub username: Option, + /// Password for Basic auth (required when auth_type is Basic) + pub password: Option, + /// Token for Bearer auth (required when auth_type is Bearer) + pub token: Option, +} + +impl RegistryAuth { + fn to_native(&self) -> Result { + match self.auth_type { + RegistryAuthType::Anonymous => Ok(NativeRegistryAuth::Anonymous), + RegistryAuthType::Basic => { + let username = self + .username + .clone() + .ok_or_else(|| Error::from_reason("username required for Basic auth"))?; + let password = self + .password + .clone() + .ok_or_else(|| Error::from_reason("password required for Basic auth"))?; + Ok(NativeRegistryAuth::Basic(username, password)) + } + RegistryAuthType::Bearer => { + let token = self + .token + .clone() + .ok_or_else(|| Error::from_reason("token required for Bearer auth"))?; + Ok(NativeRegistryAuth::Bearer(token)) + } + } + } +} + +// ============================================================================ +// Client Configuration Types - Mirror ClientConfig exactly +// ============================================================================ + +/// Protocol configuration for the client. +/// Mirrors the native ClientProtocol enum. +#[napi(string_enum)] +pub enum ClientProtocol { + /// Use HTTP (insecure) + Http, + /// Use HTTPS (secure, default) + Https, + /// Use HTTPS except for specified registries + HttpsExcept, +} + +/// Certificate encoding format. +/// Mirrors the native CertificateEncoding enum. +#[napi(string_enum)] +pub enum CertificateEncoding { + /// DER encoded certificate + Der, + /// PEM encoded certificate + Pem, +} + +/// A x509 certificate for TLS. +/// Mirrors the native Certificate struct. +#[napi(object)] +pub struct Certificate { + /// Which encoding is used by the certificate + pub encoding: CertificateEncoding, + /// Certificate data as bytes + pub data: Buffer, +} + +impl Certificate { + fn to_native(&self) -> NativeCertificate { + NativeCertificate { + encoding: match self.encoding { + CertificateEncoding::Der => NativeCertificateEncoding::Der, + CertificateEncoding::Pem => NativeCertificateEncoding::Pem, + }, + data: self.data.to_vec(), + } + } +} + +/// Platform filter for selecting a specific platform from multi-platform images. +/// When set, the client will automatically select the matching platform from Image Index manifests. +#[napi(object)] +pub struct PlatformFilter { + /// Operating system (e.g., "linux", "windows", "darwin") + pub os: String, + /// CPU architecture (e.g., "amd64", "arm64", "arm") + pub architecture: String, + /// Optional variant (e.g., "v7" for arm/v7) + pub variant: Option, +} + +/// Client configuration options. +/// Mirrors the native ClientConfig struct with all available options. +#[napi(object)] +pub struct ClientConfig { + /// Which protocol the client should use (default: Https) + pub protocol: Option, + /// List of registries to exclude from HTTPS (used with HttpsExcept protocol) + pub https_except_registries: Option>, + /// Accept invalid certificates (default: false) + pub accept_invalid_certificates: Option, + /// Use monolithic push for pushing blobs (default: false) + pub use_monolithic_push: Option, + /// Extra root certificates to trust (for self-signed certificates) + pub extra_root_certificates: Option>, + /// Maximum number of concurrent uploads during push (default: 16) + pub max_concurrent_upload: Option, + /// Maximum number of concurrent downloads during pull (default: 16) + pub max_concurrent_download: Option, + /// Default token expiration in seconds (default: 60) + pub default_token_expiration_secs: Option, + /// Read timeout in milliseconds + pub read_timeout_ms: Option, + /// Connect timeout in milliseconds + pub connect_timeout_ms: Option, + /// HTTPS proxy URL + pub https_proxy: Option, + /// HTTP proxy URL + pub http_proxy: Option, + /// No proxy list (comma-separated) + pub no_proxy: Option, + /// Platform filter for multi-platform image selection. + /// When set, automatically selects the matching platform from Image Index manifests. + pub platform: Option, +} + +impl ClientConfig { + fn to_native(&self) -> NativeClientConfig { + let mut config = NativeClientConfig::default(); + + if let Some(protocol) = &self.protocol { + config.protocol = match protocol { + ClientProtocol::Http => NativeClientProtocol::Http, + ClientProtocol::Https => NativeClientProtocol::Https, + ClientProtocol::HttpsExcept => { + let registries = self.https_except_registries.clone().unwrap_or_default(); + NativeClientProtocol::HttpsExcept(registries) + } + }; + } + + if let Some(accept) = self.accept_invalid_certificates { + config.accept_invalid_certificates = accept; + } + + if let Some(monolithic) = self.use_monolithic_push { + config.use_monolithic_push = monolithic; + } + + if let Some(certs) = &self.extra_root_certificates { + config.extra_root_certificates = certs.iter().map(|c| c.to_native()).collect(); + } + + if let Some(max) = self.max_concurrent_upload { + config.max_concurrent_upload = max as usize; + } + + if let Some(max) = self.max_concurrent_download { + config.max_concurrent_download = max as usize; + } + + if let Some(secs) = self.default_token_expiration_secs { + config.default_token_expiration_secs = secs as usize; + } + + if let Some(ms) = self.read_timeout_ms { + config.read_timeout = Some(Duration::from_millis(ms as u64)); + } + + if let Some(ms) = self.connect_timeout_ms { + config.connect_timeout = Some(Duration::from_millis(ms as u64)); + } + + if let Some(proxy) = &self.https_proxy { + config.https_proxy = Some(proxy.clone()); + } + + if let Some(proxy) = &self.http_proxy { + config.http_proxy = Some(proxy.clone()); + } + + if let Some(no_proxy) = &self.no_proxy { + config.no_proxy = Some(no_proxy.clone()); + } + + if let Some(p) = &self.platform { + let os = Os::from(p.os.as_str()); + let arch = Arch::from(p.architecture.as_str()); + let variant = p.variant.clone(); + config.platform_resolver = Some(Box::new(move |manifests| { + manifests + .iter() + .find(|e| { + e.platform.as_ref().is_some_and(|plat| { + plat.os == os + && plat.architecture == arch + && (variant.is_none() || plat.variant == variant) + }) + }) + .map(|e| e.digest.clone()) + })); + } + + config + } +} + +// ============================================================================ +// Data Types - Mirror ImageLayer, Config, ImageData, PushResponse exactly +// ============================================================================ + +/// An image layer with data and metadata. +/// Mirrors the native ImageLayer struct. +#[napi(object)] +pub struct ImageLayer { + /// The layer data as raw bytes + pub data: Buffer, + /// The media type of this layer + pub media_type: String, + /// Optional annotations for this layer + pub annotations: Option>, +} + +impl ImageLayer { + fn from_native(layer: NativeImageLayer) -> Self { + ImageLayer { + data: Buffer::from(layer.data.to_vec()), + media_type: layer.media_type, + annotations: layer.annotations, + } + } + + fn to_native(&self) -> NativeImageLayer { + NativeImageLayer::new( + self.data.to_vec(), + self.media_type.clone(), + self.annotations.clone(), + ) + } +} + +/// Configuration object for an image. +/// Mirrors the native Config struct. +#[napi(object)] +pub struct Config { + /// The config data as raw bytes + pub data: Buffer, + /// The media type of this config + pub media_type: String, + /// Optional annotations for this config + pub annotations: Option>, +} + +impl Config { + fn from_native(config: NativeConfig) -> Self { + Config { + data: Buffer::from(config.data.to_vec()), + media_type: config.media_type, + annotations: config.annotations, + } + } + + fn to_native(&self) -> NativeConfig { + NativeConfig::new( + self.data.to_vec(), + self.media_type.clone(), + self.annotations.clone(), + ) + } +} + +/// Data returned from pulling an image. +/// Mirrors the native ImageData struct. +#[napi(object)] +pub struct ImageData { + /// The layers of the image + pub layers: Vec, + /// The digest of the image (if available) + pub digest: Option, + /// The configuration object of the image + pub config: Config, + /// The manifest (if available) + pub manifest: Option, +} + +impl ImageData { + fn from_native(data: NativeImageData) -> Self { + ImageData { + layers: data + .layers + .into_iter() + .map(ImageLayer::from_native) + .collect(), + digest: data.digest, + config: Config::from_native(data.config), + manifest: data.manifest.map(|m| m.into()), + } + } +} + +/// Response from pushing an image. +/// Mirrors the native PushResponse struct. +#[napi(object)] +pub struct PushResponse { + /// Pullable URL for the config + pub config_url: String, + /// Pullable URL for the manifest + pub manifest_url: String, +} + +impl From for PushResponse { + fn from(resp: NativePushResponse) -> Self { + PushResponse { + config_url: resp.config_url, + manifest_url: resp.manifest_url, + } + } +} + +// ============================================================================ +// Manifest Types - For structured manifest handling +// ============================================================================ + +/// OCI Descriptor - describes a content addressable resource. +#[napi(object)] +pub struct Descriptor { + /// The media type of the referenced content + pub media_type: String, + /// The digest of the targeted content + pub digest: String, + /// The size in bytes of the targeted content + pub size: i64, + /// Optional list of URLs from which this object may be downloaded + pub urls: Option>, + /// Optional annotations for this descriptor + pub annotations: Option>, +} + +impl From for Descriptor { + fn from(d: OciDescriptor) -> Self { + Descriptor { + media_type: d.media_type, + digest: d.digest, + size: d.size, + urls: d.urls, + annotations: d.annotations, + } + } +} + +impl From for OciDescriptor { + fn from(d: Descriptor) -> Self { + OciDescriptor { + media_type: d.media_type, + digest: d.digest, + size: d.size, + urls: d.urls, + annotations: d.annotations, + } + } +} + +/// Platform specification for an image. +#[napi(object)] +pub struct PlatformSpec { + /// CPU architecture + pub architecture: String, + /// Operating system + pub os: String, + /// OS version + pub os_version: Option, + /// OS features + pub os_features: Option>, + /// CPU variant + pub variant: Option, + /// Additional features + pub features: Option>, +} + +impl From for PlatformSpec { + fn from(p: Platform) -> Self { + PlatformSpec { + architecture: p.architecture.to_string(), + os: p.os.to_string(), + os_version: p.os_version, + os_features: p.os_features, + variant: p.variant, + features: p.features, + } + } +} + +impl From for Platform { + fn from(p: PlatformSpec) -> Self { + Platform { + architecture: Arch::from(p.architecture.as_str()), + os: Os::from(p.os.as_str()), + os_version: p.os_version, + os_features: p.os_features, + variant: p.variant, + features: p.features, + } + } +} + +/// An entry in an image index manifest. +#[napi(object)] +pub struct ManifestEntry { + /// Media type of the manifest + pub media_type: String, + /// Digest of the manifest + pub digest: String, + /// Size in bytes + pub size: i64, + /// Platform specification + pub platform: Option, + /// Annotations + pub annotations: Option>, +} + +impl From for ManifestEntry { + fn from(e: ImageIndexEntry) -> Self { + ManifestEntry { + media_type: e.media_type, + digest: e.digest, + size: e.size, + platform: e.platform.map(|p| p.into()), + annotations: e.annotations, + } + } +} + +impl From for ImageIndexEntry { + fn from(e: ManifestEntry) -> Self { + ImageIndexEntry { + media_type: e.media_type, + digest: e.digest, + size: e.size, + platform: e.platform.map(|p| p.into()), + annotations: e.annotations, + } + } +} + +/// OCI Image Index (manifest list). +#[napi(object)] +pub struct ImageIndex { + /// Schema version (always 2) + pub schema_version: u8, + /// Media type of this manifest + pub media_type: Option, + /// List of manifests for specific platforms + pub manifests: Vec, + /// Artifact type + pub artifact_type: Option, + /// Annotations + pub annotations: Option>, +} + +impl From for ImageIndex { + fn from(idx: OciImageIndex) -> Self { + ImageIndex { + schema_version: idx.schema_version, + media_type: idx.media_type, + manifests: idx.manifests.into_iter().map(|m| m.into()).collect(), + artifact_type: idx.artifact_type, + annotations: idx.annotations, + } + } +} + +impl From for OciImageIndex { + fn from(idx: ImageIndex) -> Self { + OciImageIndex { + schema_version: idx.schema_version, + media_type: idx.media_type, + manifests: idx.manifests.into_iter().map(|m| m.into()).collect(), + artifact_type: idx.artifact_type, + annotations: idx.annotations, + } + } +} + +/// OCI Image Manifest. +#[napi(object)] +pub struct ImageManifest { + /// Schema version (always 2) + pub schema_version: u8, + /// Media type of this manifest + pub media_type: Option, + /// The image configuration descriptor + pub config: Descriptor, + /// The image layers + pub layers: Vec, + /// Subject descriptor (for referrers) + pub subject: Option, + /// Artifact type + pub artifact_type: Option, + /// Annotations + pub annotations: Option>, +} + +impl From for ImageManifest { + fn from(m: OciImageManifest) -> Self { + ImageManifest { + schema_version: m.schema_version, + media_type: m.media_type, + config: m.config.into(), + layers: m.layers.into_iter().map(|l| l.into()).collect(), + subject: m.subject.map(|s| s.into()), + artifact_type: m.artifact_type, + annotations: m.annotations, + } + } +} + +impl From for OciImageManifest { + fn from(m: ImageManifest) -> Self { + OciImageManifest { + schema_version: m.schema_version, + media_type: m.media_type, + config: m.config.into(), + layers: m.layers.into_iter().map(|l| l.into()).collect(), + subject: m.subject.map(|s| s.into()), + artifact_type: m.artifact_type, + annotations: m.annotations, + } + } +} + +// ============================================================================ +// Union type for OciManifest (can be Image or ImageIndex) +// ============================================================================ + +/// Manifest type discriminator. +#[napi(string_enum)] +pub enum ManifestType { + /// An OCI image manifest + Image, + /// An OCI image index (manifest list) + ImageIndex, +} + +/// OCI Manifest - can be either an Image manifest or an ImageIndex. +/// Check `manifest_type` to determine which field is populated. +#[napi(object)] +pub struct Manifest { + /// The type of manifest + pub manifest_type: ManifestType, + /// The image manifest (populated when manifest_type is Image) + pub image: Option, + /// The image index (populated when manifest_type is ImageIndex) + pub image_index: Option, +} + +impl From for Manifest { + fn from(m: OciManifest) -> Self { + match m { + OciManifest::Image(img) => Manifest { + manifest_type: ManifestType::Image, + image: Some(img.into()), + image_index: None, + }, + OciManifest::ImageIndex(idx) => Manifest { + manifest_type: ManifestType::ImageIndex, + image: None, + image_index: Some(idx.into()), + }, + } + } +} + +impl TryFrom for OciManifest { + type Error = String; + + fn try_from(m: Manifest) -> std::result::Result { + match m.manifest_type { + ManifestType::Image => { + let img = m + .image + .ok_or("image field required for Image manifest type")?; + Ok(OciManifest::Image(img.into())) + } + ManifestType::ImageIndex => { + let idx = m + .image_index + .ok_or("image_index field required for ImageIndex manifest type")?; + Ok(OciManifest::ImageIndex(idx.into())) + } + } + } +} + +/// Result from pull_manifest containing both manifest and digest. +#[napi(object)] +pub struct PullManifestResult { + /// The pulled manifest + pub manifest: Manifest, + /// The digest of the manifest + pub digest: String, +} + +// ============================================================================ +// Result Types for functions that return tuples +// ============================================================================ + +/// Result from pull_image_manifest containing both manifest and digest. +#[napi(object)] +pub struct PullImageManifestResult { + /// The pulled image manifest + pub manifest: ImageManifest, + /// The digest of the manifest + pub digest: String, +} + +// ============================================================================ +// Main Client - Mirrors the native Client +// ============================================================================ + +/// OCI Distribution client for interacting with OCI registries. +/// Provides pull, push, and manifest operations. +#[napi] +pub struct OciClient { + inner: Client, +} + +#[napi] +impl OciClient { + /// Create a new OCI client with default configuration. + #[napi(constructor)] + pub fn new() -> Self { + OciClient { + inner: Client::default(), + } + } + + /// Create a new OCI client with custom configuration. + #[napi(factory)] + pub fn with_config(config: ClientConfig) -> Self { + OciClient { + inner: Client::new(config.to_native()), + } + } + + /// Pull an image from the registry. + /// + /// Arguments match native: `pull(image: &Reference, auth: &RegistryAuth, accepted_media_types: Vec<&str>)` + /// + /// Returns ImageData containing layers (as Buffers), config, and manifest. + #[napi] + pub async fn pull( + &self, + image: String, + auth: RegistryAuth, + accepted_media_types: Vec, + ) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + let native_auth = auth.to_native()?; + let media_types: Vec<&str> = accepted_media_types.iter().map(|s| s.as_str()).collect(); + + let image_data = self + .inner + .pull(&reference, &native_auth, media_types) + .await + .map_err(|e| oci_error("Pull failed", e))?; + + Ok(ImageData::from_native(image_data)) + } + + /// Push an image to the registry. + /// + /// Arguments match native: `push(image_ref: &Reference, layers: &[ImageLayer], config: Config, auth: &RegistryAuth, manifest: Option)` + /// + /// Returns PushResponse with config and manifest URLs. + #[napi] + pub async fn push( + &self, + image_ref: String, + layers: Vec, + config: Config, + auth: RegistryAuth, + manifest: Option, + ) -> Result { + let reference = Reference::from_str(&image_ref) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + let native_auth = auth.to_native()?; + let native_layers: Vec = layers.iter().map(|l| l.to_native()).collect(); + let native_config = config.to_native(); + let native_manifest: Option = manifest.map(|m| m.into()); + + let response = self + .inner + .push( + &reference, + &native_layers, + native_config, + &native_auth, + native_manifest, + ) + .await + .map_err(|e| oci_error("Push failed", e))?; + + Ok(response.into()) + } + + /// Pull referrers for an artifact (OCI 1.1 Referrers API). + /// + /// Arguments match native: `pull_referrers(image: &Reference, artifact_type: Option<&str>)` + /// + /// Returns an ImageIndex containing the referrers. + #[napi] + pub async fn pull_referrers( + &self, + image: String, + artifact_type: Option, + ) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + + let referrers = self + .inner + .pull_referrers(&reference, artifact_type.as_deref()) + .await + .map_err(|e| oci_error("Pull referrers failed", e))?; + + Ok(referrers.into()) + } + + /// Push a manifest list (image index) to the registry. + /// + /// Arguments match native: `push_manifest_list(reference: &Reference, auth: &RegistryAuth, manifest: OciImageIndex)` + /// + /// Returns the manifest URL. + #[napi] + pub async fn push_manifest_list( + &self, + reference: String, + auth: RegistryAuth, + manifest: ImageIndex, + ) -> Result { + let ref_parsed = Reference::from_str(&reference) + .map_err(|e| Error::from_reason(format!("Invalid reference: {}", e)))?; + let native_auth = auth.to_native()?; + let native_manifest: OciImageIndex = manifest.into(); + + self.inner + .push_manifest_list(&ref_parsed, &native_auth, native_manifest) + .await + .map_err(|e| oci_error("Push manifest list failed", e)) + } + + /// Pull an image manifest from the registry. + /// + /// Arguments match native: `pull_image_manifest(image: &Reference, auth: &RegistryAuth)` + /// + /// If a multi-platform Image Index manifest is encountered, a platform-specific + /// Image manifest will be selected using the client's default platform resolution. + /// + /// Returns both the manifest and its digest. + #[napi] + pub async fn pull_image_manifest( + &self, + image: String, + auth: RegistryAuth, + ) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + let native_auth = auth.to_native()?; + + let (manifest, digest) = self + .inner + .pull_image_manifest(&reference, &native_auth) + .await + .map_err(|e| oci_error("Pull image manifest failed", e))?; + + Ok(PullImageManifestResult { + manifest: manifest.into(), + digest, + }) + } + + // ======================================================================== + // Additional utility methods for complete API coverage + // ======================================================================== + + /// Store authentication credentials for a registry. + /// This is useful for pre-authenticating before multiple operations. + #[napi] + pub async fn store_auth(&self, registry: String, auth: RegistryAuth) -> Result<()> { + let native_auth = auth.to_native()?; + self.inner + .store_auth_if_needed(®istry, &native_auth) + .await; + Ok(()) + } + + /// Pull a manifest (either image or index) from the registry. + /// Returns the manifest and its digest. + #[napi] + pub async fn pull_manifest( + &self, + image: String, + auth: RegistryAuth, + ) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + let native_auth = auth.to_native()?; + + let (manifest, digest) = self + .inner + .pull_manifest(&reference, &native_auth) + .await + .map_err(|e| oci_error("Pull manifest failed", e))?; + + Ok(PullManifestResult { + manifest: manifest.into(), + digest, + }) + } + + /// Pull a manifest as raw bytes. + #[napi] + pub async fn pull_manifest_raw( + &self, + image: String, + auth: RegistryAuth, + accepted_media_types: Vec, + ) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + let native_auth = auth.to_native()?; + let media_types: Vec<&str> = accepted_media_types.iter().map(|s| s.as_str()).collect(); + + let (bytes, _digest) = self + .inner + .pull_manifest_raw(&reference, &native_auth, &media_types) + .await + .map_err(|e| oci_error("Pull manifest raw failed", e))?; + + Ok(Buffer::from(bytes.to_vec())) + } + + /// Push a manifest to the registry. + /// Returns the manifest URL. + #[napi] + pub async fn push_manifest(&self, image: String, manifest: Manifest) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + + let native_manifest: OciManifest = manifest + .try_into() + .map_err(|e: String| Error::from_reason(e))?; + + self.inner + .push_manifest(&reference, &native_manifest) + .await + .map_err(|e| oci_error("Push manifest failed", e)) + } + + /// Push a blob to the registry. + /// Returns the blob digest. + #[napi] + pub async fn push_blob(&self, image: String, data: Buffer, digest: String) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + + self.inner + .push_blob(&reference, data.to_vec(), &digest) + .await + .map_err(|e| oci_error("Push blob failed", e)) + } + + /// Pull a blob from the registry. + /// Returns the blob data. + #[napi] + pub async fn pull_blob(&self, image: String, digest: String) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + + let mut data = Vec::new(); + self.inner + .pull_blob(&reference, digest.as_str(), &mut data) + .await + .map_err(|e| oci_error("Pull blob failed", e))?; + + Ok(Buffer::from(data)) + } + + /// Check if a blob exists in the registry. + #[napi] + pub async fn blob_exists(&self, image: String, digest: String) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + + self.inner + .blob_exists(&reference, &digest) + .await + .map_err(|e| oci_error("Blob exists check failed", e)) + } + + /// Mount a blob from another repository. + #[napi] + pub async fn mount_blob(&self, target: String, source: String, digest: String) -> Result<()> { + let target_ref = Reference::from_str(&target) + .map_err(|e| Error::from_reason(format!("Invalid target reference: {}", e)))?; + let source_ref = Reference::from_str(&source) + .map_err(|e| Error::from_reason(format!("Invalid source reference: {}", e)))?; + + self.inner + .mount_blob(&target_ref, &source_ref, &digest) + .await + .map_err(|e| oci_error("Mount blob failed", e)) + } + + /// List tags for a repository. + #[napi] + pub async fn list_tags( + &self, + image: String, + auth: RegistryAuth, + n: Option, + last: Option, + ) -> Result> { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + let native_auth = auth.to_native()?; + + let tags = self + .inner + .list_tags( + &reference, + &native_auth, + n.map(|v| v as usize), + last.as_deref(), + ) + .await + .map_err(|e| oci_error("List tags failed", e))?; + + Ok(tags.tags) + } + + /// Fetch manifest digest without downloading the full manifest. + #[napi] + pub async fn fetch_manifest_digest(&self, image: String, auth: RegistryAuth) -> Result { + let reference = Reference::from_str(&image) + .map_err(|e| Error::from_reason(format!("Invalid image reference: {}", e)))?; + let native_auth = auth.to_native()?; + + self.inner + .fetch_manifest_digest(&reference, &native_auth) + .await + .map_err(|e| oci_error("Fetch manifest digest failed", e)) + } +} + +// ============================================================================ +// OCI Annotation Constants +// ============================================================================ + +/// Date and time on which the image was built (string, date-time as defined by RFC 3339) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_CREATED: &str = "org.opencontainers.image.created"; + +/// Contact details of the people or organization responsible for the image (freeform string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_AUTHORS: &str = "org.opencontainers.image.authors"; + +/// URL to find more information on the image (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_URL: &str = "org.opencontainers.image.url"; + +/// URL to get documentation on the image (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_DOCUMENTATION: &str = "org.opencontainers.image.documentation"; + +/// URL to get source code for building the image (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_SOURCE: &str = "org.opencontainers.image.source"; + +/// Version of the packaged software +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_VERSION: &str = "org.opencontainers.image.version"; + +/// Source control revision identifier for the packaged software +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_REVISION: &str = "org.opencontainers.image.revision"; + +/// Name of the distributing entity, organization or individual +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_VENDOR: &str = "org.opencontainers.image.vendor"; + +/// License(s) under which contained software is distributed as an SPDX License Expression +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_LICENSES: &str = "org.opencontainers.image.licenses"; + +/// Name of the reference for a target (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_REF_NAME: &str = "org.opencontainers.image.ref.name"; + +/// Human-readable title of the image (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_TITLE: &str = "org.opencontainers.image.title"; + +/// Human-readable description of the software packaged in the image (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_DESCRIPTION: &str = "org.opencontainers.image.description"; + +/// Digest of the image this image is based on (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_BASE_DIGEST: &str = "org.opencontainers.image.base.digest"; + +/// Image reference of the image this image is based on (string) +#[napi] +pub const ORG_OPENCONTAINERS_IMAGE_BASE_NAME: &str = "org.opencontainers.image.base.name"; + +// ============================================================================ +// OCI Media Type Constants +// ============================================================================ + +/// The mediatype for WASM layers +#[napi] +pub const WASM_LAYER_MEDIA_TYPE: &str = "application/vnd.wasm.content.layer.v1+wasm"; + +/// The mediatype for a WASM image config +#[napi] +pub const WASM_CONFIG_MEDIA_TYPE: &str = "application/vnd.wasm.config.v1+json"; + +/// The mediatype for a Docker v2 schema 2 manifest +#[napi] +pub const IMAGE_MANIFEST_MEDIA_TYPE: &str = "application/vnd.docker.distribution.manifest.v2+json"; + +/// The mediatype for a Docker v2 schema 2 manifest list +#[napi] +pub const IMAGE_MANIFEST_LIST_MEDIA_TYPE: &str = + "application/vnd.docker.distribution.manifest.list.v2+json"; + +/// The mediatype for an OCI image index manifest +#[napi] +pub const OCI_IMAGE_INDEX_MEDIA_TYPE: &str = "application/vnd.oci.image.index.v1+json"; + +/// The mediatype for an OCI image manifest +#[napi] +pub const OCI_IMAGE_MEDIA_TYPE: &str = "application/vnd.oci.image.manifest.v1+json"; + +/// The mediatype for an image config (manifest) +#[napi] +pub const IMAGE_CONFIG_MEDIA_TYPE: &str = "application/vnd.oci.image.config.v1+json"; + +/// The mediatype that Docker uses for image configs +#[napi] +pub const IMAGE_DOCKER_CONFIG_MEDIA_TYPE: &str = "application/vnd.docker.container.image.v1+json"; + +/// The mediatype for a layer +#[napi] +pub const IMAGE_LAYER_MEDIA_TYPE: &str = "application/vnd.oci.image.layer.v1.tar"; + +/// The mediatype for a layer that is gzipped +#[napi] +pub const IMAGE_LAYER_GZIP_MEDIA_TYPE: &str = "application/vnd.oci.image.layer.v1.tar+gzip"; + +/// The mediatype that Docker uses for a layer that is tarred +#[napi] +pub const IMAGE_DOCKER_LAYER_TAR_MEDIA_TYPE: &str = "application/vnd.docker.image.rootfs.diff.tar"; + +/// The mediatype that Docker uses for a layer that is gzipped +#[napi] +pub const IMAGE_DOCKER_LAYER_GZIP_MEDIA_TYPE: &str = + "application/vnd.docker.image.rootfs.diff.tar.gzip"; + +/// The mediatype for a layer that is nondistributable +#[napi] +pub const IMAGE_LAYER_NONDISTRIBUTABLE_MEDIA_TYPE: &str = + "application/vnd.oci.image.layer.nondistributable.v1.tar"; + +/// The mediatype for a layer that is nondistributable and gzipped +#[napi] +pub const IMAGE_LAYER_NONDISTRIBUTABLE_GZIP_MEDIA_TYPE: &str = + "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip"; + +// ============================================================================ +// Helper functions +// ============================================================================ + +/// Create an anonymous authentication object. +#[napi] +pub fn anonymous_auth() -> RegistryAuth { + RegistryAuth { + auth_type: RegistryAuthType::Anonymous, + username: None, + password: None, + token: None, + } +} + +/// Create a basic authentication object. +#[napi] +pub fn basic_auth(username: String, password: String) -> RegistryAuth { + RegistryAuth { + auth_type: RegistryAuthType::Basic, + username: Some(username), + password: Some(password), + token: None, + } +} + +/// Create a bearer token authentication object. +#[napi] +pub fn bearer_auth(token: String) -> RegistryAuth { + RegistryAuth { + auth_type: RegistryAuthType::Bearer, + username: None, + password: None, + token: Some(token), + } +} diff --git a/bindings/nodejs/tsconfig.json b/bindings/nodejs/tsconfig.json new file mode 100644 index 00000000..a2a16263 --- /dev/null +++ b/bindings/nodejs/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "strict": true, + "moduleResolution": "node", + "module": "CommonJS", + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "include": ["."], + "exclude": ["node_modules", "__test__"] +} diff --git a/bindings/nodejs/yarn.lock b/bindings/nodejs/yarn.lock new file mode 100644 index 00000000..361b246a --- /dev/null +++ b/bindings/nodejs/yarn.lock @@ -0,0 +1,2910 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@emnapi/core@npm:^1.7.1": + version: 1.7.1 + resolution: "@emnapi/core@npm:1.7.1" + dependencies: + "@emnapi/wasi-threads": "npm:1.1.0" + tslib: "npm:^2.4.0" + checksum: 10c0/f3740be23440b439333e3ae3832163f60c96c4e35337f3220ceba88f36ee89a57a871d27c94eb7a9ff98a09911ed9a2089e477ab549f4d30029f8b907f84a351 + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.7.1": + version: 1.7.1 + resolution: "@emnapi/runtime@npm:1.7.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/26b851cd3e93877d8732a985a2ebf5152325bbacc6204ef5336a47359dedcc23faeb08cdfcb8bb389b5401b3e894b882bc1a1e55b4b7c1ed1e67c991a760ddd5 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.1.0": + version: 1.1.0 + resolution: "@emnapi/wasi-threads@npm:1.1.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/e6d54bf2b1e64cdd83d2916411e44e579b6ae35d5def0dea61a3c452d9921373044dff32a8b8473ae60c80692bdc39323e98b96a3f3d87ba6886b24dd0ef7ca1 + languageName: node + linkType: hard + +"@inquirer/ansi@npm:^2.0.2": + version: 2.0.2 + resolution: "@inquirer/ansi@npm:2.0.2" + checksum: 10c0/dd51378cbe358fb968fb5a686e88bede90930cf17ee75ea158f410dbef4ff2a108026159ee38699489c2247b29bedb8100d1f6081a8b63213aaba2ffc6ff2287 + languageName: node + linkType: hard + +"@inquirer/checkbox@npm:^5.0.2": + version: 5.0.2 + resolution: "@inquirer/checkbox@npm:5.0.2" + dependencies: + "@inquirer/ansi": "npm:^2.0.2" + "@inquirer/core": "npm:^11.0.2" + "@inquirer/figures": "npm:^2.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/d226adaab599652d413eb50e33bda686dc43514e887900577fd798843a72000a9d775572fe80c5eeffa006e384a1ab653b7ed1eebca2cb66f7cf76db8803864e + languageName: node + linkType: hard + +"@inquirer/confirm@npm:^6.0.2": + version: 6.0.2 + resolution: "@inquirer/confirm@npm:6.0.2" + dependencies: + "@inquirer/core": "npm:^11.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/e6e0726af4abef856e1db9164b461ed492a222bc5aab4a8aef04ffe89484dddac90d05630901c8ec0b6d13e36d8e130c158eb55f95c7d6e22f6e97f6def8b848 + languageName: node + linkType: hard + +"@inquirer/core@npm:^11.0.2": + version: 11.0.2 + resolution: "@inquirer/core@npm:11.0.2" + dependencies: + "@inquirer/ansi": "npm:^2.0.2" + "@inquirer/figures": "npm:^2.0.2" + "@inquirer/type": "npm:^4.0.2" + cli-width: "npm:^4.1.0" + mute-stream: "npm:^3.0.0" + signal-exit: "npm:^4.1.0" + wrap-ansi: "npm:^9.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/8feaa17d76641af0d3b74ca14dffbc044dc4a283d87ba401fe1823d9a5f3ec20b32c94125e9601277bdabfcfbf09e650c88d0b348414c877209c0eb31808f7d5 + languageName: node + linkType: hard + +"@inquirer/editor@npm:^5.0.2": + version: 5.0.2 + resolution: "@inquirer/editor@npm:5.0.2" + dependencies: + "@inquirer/core": "npm:^11.0.2" + "@inquirer/external-editor": "npm:^2.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/04bb8413f1d3ad9cc05795c3e404023c763e8d20e21c20860a2dc8e875b1ee38c397454179cb10accac9b3395c83bb0a05d2ffa2a7cb2b8085ab0e057ecd74f8 + languageName: node + linkType: hard + +"@inquirer/expand@npm:^5.0.2": + version: 5.0.2 + resolution: "@inquirer/expand@npm:5.0.2" + dependencies: + "@inquirer/core": "npm:^11.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/59acf3973a673addfb3bceb635c2474147533b1baa5d456137e7aefe082c57c3cd3cc4e5f80632787cbe8d46d6e55947cd2f1e00cda2e82e052dcf69e54c2b72 + languageName: node + linkType: hard + +"@inquirer/external-editor@npm:^2.0.2": + version: 2.0.2 + resolution: "@inquirer/external-editor@npm:2.0.2" + dependencies: + chardet: "npm:^2.1.1" + iconv-lite: "npm:^0.7.0" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/f1f0eea821cd10ff602394f71a3ad77f31959cb86dc35716b6a9f94052f70692ed8402fc8288970c5499b5fba2cb1094c6a75d0c098fdc03afb60a3766a7a625 + languageName: node + linkType: hard + +"@inquirer/figures@npm:^2.0.2": + version: 2.0.2 + resolution: "@inquirer/figures@npm:2.0.2" + checksum: 10c0/12353dc765c001bc7df4d4570eaa25dba53af4553d02d231e69104e289918327ca3af3626a29885616d35e5d397fc3bfd4bbcc3e2c8d668d3064dd30c3fdf43b + languageName: node + linkType: hard + +"@inquirer/input@npm:^5.0.2": + version: 5.0.2 + resolution: "@inquirer/input@npm:5.0.2" + dependencies: + "@inquirer/core": "npm:^11.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/0be1892a10c23392b097b116892674284f0dbd8f484309711bbf770eaff35337b5a45bf32e75b8391b2cce28b6ca46168f850235c6284d8becd1794925d430fb + languageName: node + linkType: hard + +"@inquirer/number@npm:^4.0.2": + version: 4.0.2 + resolution: "@inquirer/number@npm:4.0.2" + dependencies: + "@inquirer/core": "npm:^11.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/6d90ee99a6cbd5d47e46741fef894c48ec71e823105a1302c4adbff1cf02c23fb7b3f5cdab0c2fad0f49f9047425067b0573e23289b069c08e06fa60f6c40c89 + languageName: node + linkType: hard + +"@inquirer/password@npm:^5.0.2": + version: 5.0.2 + resolution: "@inquirer/password@npm:5.0.2" + dependencies: + "@inquirer/ansi": "npm:^2.0.2" + "@inquirer/core": "npm:^11.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/452708fe6f161894e74b08a052e9f314e1dc9c8a00c18a0c924979bdee73982ea0049eeeb538d8f2f14afade09febebdc19058c7b300ee3e4ad95f44380bd825 + languageName: node + linkType: hard + +"@inquirer/prompts@npm:^8.0.0": + version: 8.0.2 + resolution: "@inquirer/prompts@npm:8.0.2" + dependencies: + "@inquirer/checkbox": "npm:^5.0.2" + "@inquirer/confirm": "npm:^6.0.2" + "@inquirer/editor": "npm:^5.0.2" + "@inquirer/expand": "npm:^5.0.2" + "@inquirer/input": "npm:^5.0.2" + "@inquirer/number": "npm:^4.0.2" + "@inquirer/password": "npm:^5.0.2" + "@inquirer/rawlist": "npm:^5.0.2" + "@inquirer/search": "npm:^4.0.2" + "@inquirer/select": "npm:^5.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/155ff5144eaf1be5a6e8245f5fc5e6848bd437d3875ee7e10e51770ea2a190f323a12663f3f7778734f1c38ac7861c90c365bd01cfd8acbf3cd81286e5a8bb3d + languageName: node + linkType: hard + +"@inquirer/rawlist@npm:^5.0.2": + version: 5.0.2 + resolution: "@inquirer/rawlist@npm:5.0.2" + dependencies: + "@inquirer/core": "npm:^11.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/65accf530accfb137a054cef3a8e19c940db363bf141bd8a603a379a6f453047e8495b621e4af72558df8cd2953cce69b3c73d4e4cfbe653b474b4ce99636761 + languageName: node + linkType: hard + +"@inquirer/search@npm:^4.0.2": + version: 4.0.2 + resolution: "@inquirer/search@npm:4.0.2" + dependencies: + "@inquirer/core": "npm:^11.0.2" + "@inquirer/figures": "npm:^2.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/799374e12f3130031cd292a7f9a486abb4fcaf7b86f8182d0f6dae54b9cf31e20d032754b562c4c6977404ec91fe8e638fcda7be3b7df6b83ff872ae7668eef4 + languageName: node + linkType: hard + +"@inquirer/select@npm:^5.0.2": + version: 5.0.2 + resolution: "@inquirer/select@npm:5.0.2" + dependencies: + "@inquirer/ansi": "npm:^2.0.2" + "@inquirer/core": "npm:^11.0.2" + "@inquirer/figures": "npm:^2.0.2" + "@inquirer/type": "npm:^4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/2a0dda3c75ad73048a4913d317a95e7cbff62da41f6e9bcdc54cd64db2ffcd1548637371d2864594cbf4534e871299a266b44e5bac705241e7c5094445a39bd8 + languageName: node + linkType: hard + +"@inquirer/type@npm:^4.0.2": + version: 4.0.2 + resolution: "@inquirer/type@npm:4.0.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/cebf454dbed948809025d64807e60e333df6b9c8eac1090a1c4c07e51a9a03ffbe295ebb723ce1029cf28e66a1c6822e8290702dec006170cce4ff39264316f5 + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@mapbox/node-pre-gyp@npm:^2.0.0": + version: 2.0.3 + resolution: "@mapbox/node-pre-gyp@npm:2.0.3" + dependencies: + consola: "npm:^3.2.3" + detect-libc: "npm:^2.0.0" + https-proxy-agent: "npm:^7.0.5" + node-fetch: "npm:^2.6.7" + nopt: "npm:^8.0.0" + semver: "npm:^7.5.3" + tar: "npm:^7.4.0" + bin: + node-pre-gyp: bin/node-pre-gyp + checksum: 10c0/6243c60f0d7327772c679aad20f17bcbf771b362fb7fbf1ae6dcf8dd379bb852e5af26180ab1e987a75dffa7cd5445414dc47e2acbcfe820cb08374deebecbc3 + languageName: node + linkType: hard + +"@napi-rs/cli@npm:^3.2.0": + version: 3.5.0 + resolution: "@napi-rs/cli@npm:3.5.0" + dependencies: + "@inquirer/prompts": "npm:^8.0.0" + "@napi-rs/cross-toolchain": "npm:^1.0.3" + "@napi-rs/wasm-tools": "npm:^1.0.1" + "@octokit/rest": "npm:^22.0.1" + clipanion: "npm:^4.0.0-rc.4" + colorette: "npm:^2.0.20" + emnapi: "npm:^1.7.1" + es-toolkit: "npm:^1.41.0" + js-yaml: "npm:^4.1.0" + obug: "npm:^2.0.0" + semver: "npm:^7.7.3" + typanion: "npm:^3.14.0" + peerDependencies: + "@emnapi/runtime": ^1.7.1 + peerDependenciesMeta: + "@emnapi/runtime": + optional: true + bin: + napi: dist/cli.js + napi-raw: cli.mjs + checksum: 10c0/e1556cb547ba2a2df6fbe585d32f9eaf0c857bb727673950c9e6574dce8fd63c8dfd37600313ed86a317895eaf52fca90872a8e7a348f8abab7ab7d6f817336d + languageName: node + linkType: hard + +"@napi-rs/cross-toolchain@npm:^1.0.3": + version: 1.0.3 + resolution: "@napi-rs/cross-toolchain@npm:1.0.3" + dependencies: + "@napi-rs/lzma": "npm:^1.4.5" + "@napi-rs/tar": "npm:^1.1.0" + debug: "npm:^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 + checksum: 10c0/fcc7877c1e47ba6bf4801a4154240d3130703524b1fed17e736126ce58b53960872b9933f8434e3e86b4635e4c7fe881228be0d237210dd96c2087244523750f + languageName: node + linkType: hard + +"@napi-rs/lzma-android-arm-eabi@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-android-arm-eabi@npm:1.4.5" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-android-arm64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-android-arm64@npm:1.4.5" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-arm64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-darwin-arm64@npm:1.4.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-x64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-darwin-x64@npm:1.4.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-freebsd-x64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-freebsd-x64@npm:1.4.5" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm-gnueabihf@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-arm-gnueabihf@npm:1.4.5" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-arm64-gnu@npm:1.4.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-musl@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-arm64-musl@npm:1.4.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-ppc64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-ppc64-gnu@npm:1.4.5" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-riscv64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-riscv64-gnu@npm:1.4.5" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-s390x-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-s390x-gnu@npm:1.4.5" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-x64-gnu@npm:1.4.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-musl@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-x64-musl@npm:1.4.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-wasm32-wasi@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-wasm32-wasi@npm:1.4.5" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.0.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-arm64-msvc@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-win32-arm64-msvc@npm:1.4.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-ia32-msvc@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-win32-ia32-msvc@npm:1.4.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-x64-msvc@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-win32-x64-msvc@npm:1.4.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma@npm:^1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma@npm:1.4.5" + dependencies: + "@napi-rs/lzma-android-arm-eabi": "npm:1.4.5" + "@napi-rs/lzma-android-arm64": "npm:1.4.5" + "@napi-rs/lzma-darwin-arm64": "npm:1.4.5" + "@napi-rs/lzma-darwin-x64": "npm:1.4.5" + "@napi-rs/lzma-freebsd-x64": "npm:1.4.5" + "@napi-rs/lzma-linux-arm-gnueabihf": "npm:1.4.5" + "@napi-rs/lzma-linux-arm64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-arm64-musl": "npm:1.4.5" + "@napi-rs/lzma-linux-ppc64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-riscv64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-s390x-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-x64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-x64-musl": "npm:1.4.5" + "@napi-rs/lzma-wasm32-wasi": "npm:1.4.5" + "@napi-rs/lzma-win32-arm64-msvc": "npm:1.4.5" + "@napi-rs/lzma-win32-ia32-msvc": "npm:1.4.5" + "@napi-rs/lzma-win32-x64-msvc": "npm:1.4.5" + dependenciesMeta: + "@napi-rs/lzma-android-arm-eabi": + optional: true + "@napi-rs/lzma-android-arm64": + optional: true + "@napi-rs/lzma-darwin-arm64": + optional: true + "@napi-rs/lzma-darwin-x64": + optional: true + "@napi-rs/lzma-freebsd-x64": + optional: true + "@napi-rs/lzma-linux-arm-gnueabihf": + optional: true + "@napi-rs/lzma-linux-arm64-gnu": + optional: true + "@napi-rs/lzma-linux-arm64-musl": + optional: true + "@napi-rs/lzma-linux-ppc64-gnu": + optional: true + "@napi-rs/lzma-linux-riscv64-gnu": + optional: true + "@napi-rs/lzma-linux-s390x-gnu": + optional: true + "@napi-rs/lzma-linux-x64-gnu": + optional: true + "@napi-rs/lzma-linux-x64-musl": + optional: true + "@napi-rs/lzma-wasm32-wasi": + optional: true + "@napi-rs/lzma-win32-arm64-msvc": + optional: true + "@napi-rs/lzma-win32-ia32-msvc": + optional: true + "@napi-rs/lzma-win32-x64-msvc": + optional: true + checksum: 10c0/df098c99f904b54541e3a34feeb5878c98a4abf1ababf1d301d903b99d98402fff5eda49e1dd103bb4bf44a9217a5e1b17fb30b74044416561f8fe02ca098ee3 + languageName: node + linkType: hard + +"@napi-rs/tar-android-arm-eabi@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-android-arm-eabi@npm:1.1.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-android-arm64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-android-arm64@npm:1.1.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-arm64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-darwin-arm64@npm:1.1.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-x64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-darwin-x64@npm:1.1.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-freebsd-x64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-freebsd-x64@npm:1.1.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm-gnueabihf@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-arm-gnueabihf@npm:1.1.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-arm64-gnu@npm:1.1.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-musl@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-arm64-musl@npm:1.1.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-linux-ppc64-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-ppc64-gnu@npm:1.1.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-s390x-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-s390x-gnu@npm:1.1.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-x64-gnu@npm:1.1.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-musl@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-x64-musl@npm:1.1.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-wasm32-wasi@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-wasm32-wasi@npm:1.1.0" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.0.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-arm64-msvc@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-win32-arm64-msvc@npm:1.1.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-ia32-msvc@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-win32-ia32-msvc@npm:1.1.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-x64-msvc@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-win32-x64-msvc@npm:1.1.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar@npm:^1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar@npm:1.1.0" + dependencies: + "@napi-rs/tar-android-arm-eabi": "npm:1.1.0" + "@napi-rs/tar-android-arm64": "npm:1.1.0" + "@napi-rs/tar-darwin-arm64": "npm:1.1.0" + "@napi-rs/tar-darwin-x64": "npm:1.1.0" + "@napi-rs/tar-freebsd-x64": "npm:1.1.0" + "@napi-rs/tar-linux-arm-gnueabihf": "npm:1.1.0" + "@napi-rs/tar-linux-arm64-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-arm64-musl": "npm:1.1.0" + "@napi-rs/tar-linux-ppc64-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-s390x-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-x64-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-x64-musl": "npm:1.1.0" + "@napi-rs/tar-wasm32-wasi": "npm:1.1.0" + "@napi-rs/tar-win32-arm64-msvc": "npm:1.1.0" + "@napi-rs/tar-win32-ia32-msvc": "npm:1.1.0" + "@napi-rs/tar-win32-x64-msvc": "npm:1.1.0" + dependenciesMeta: + "@napi-rs/tar-android-arm-eabi": + optional: true + "@napi-rs/tar-android-arm64": + optional: true + "@napi-rs/tar-darwin-arm64": + optional: true + "@napi-rs/tar-darwin-x64": + optional: true + "@napi-rs/tar-freebsd-x64": + optional: true + "@napi-rs/tar-linux-arm-gnueabihf": + optional: true + "@napi-rs/tar-linux-arm64-gnu": + optional: true + "@napi-rs/tar-linux-arm64-musl": + optional: true + "@napi-rs/tar-linux-ppc64-gnu": + optional: true + "@napi-rs/tar-linux-s390x-gnu": + optional: true + "@napi-rs/tar-linux-x64-gnu": + optional: true + "@napi-rs/tar-linux-x64-musl": + optional: true + "@napi-rs/tar-wasm32-wasi": + optional: true + "@napi-rs/tar-win32-arm64-msvc": + optional: true + "@napi-rs/tar-win32-ia32-msvc": + optional: true + "@napi-rs/tar-win32-x64-msvc": + optional: true + checksum: 10c0/88a0ab081eacfa235266f14a0bc408b7581058b1f7e18b118c6f8e7012cca0dd91c5baf5de84e1d2eb8070386a7380aa4d8dedfc6f81e24ae9d0287ff50ae153 + languageName: node + linkType: hard + +"@napi-rs/wasm-runtime@npm:^1.0.3, @napi-rs/wasm-runtime@npm:^1.0.7": + version: 1.1.0 + resolution: "@napi-rs/wasm-runtime@npm:1.1.0" + dependencies: + "@emnapi/core": "npm:^1.7.1" + "@emnapi/runtime": "npm:^1.7.1" + "@tybys/wasm-util": "npm:^0.10.1" + checksum: 10c0/ee351052123bfc635c4cef03ac273a686522394ccd513b1e5b7b3823cecd6abb4a31f23a3a962933192b87eb7b7c3eb3def7748bd410edc66f932d90cf44e9ab + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-android-arm-eabi@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-android-arm-eabi@npm:1.0.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-android-arm64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-android-arm64@npm:1.0.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-darwin-arm64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-darwin-arm64@npm:1.0.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-darwin-x64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-darwin-x64@npm:1.0.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-freebsd-x64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-freebsd-x64@npm:1.0.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-arm64-gnu@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-arm64-gnu@npm:1.0.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-arm64-musl@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-arm64-musl@npm:1.0.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-x64-gnu@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-x64-gnu@npm:1.0.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-x64-musl@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-x64-musl@npm:1.0.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-wasm32-wasi@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-wasm32-wasi@npm:1.0.1" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.0.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-win32-arm64-msvc@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-win32-arm64-msvc@npm:1.0.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-win32-ia32-msvc@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-win32-ia32-msvc@npm:1.0.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-win32-x64-msvc@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-win32-x64-msvc@npm:1.0.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools@npm:^1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools@npm:1.0.1" + dependencies: + "@napi-rs/wasm-tools-android-arm-eabi": "npm:1.0.1" + "@napi-rs/wasm-tools-android-arm64": "npm:1.0.1" + "@napi-rs/wasm-tools-darwin-arm64": "npm:1.0.1" + "@napi-rs/wasm-tools-darwin-x64": "npm:1.0.1" + "@napi-rs/wasm-tools-freebsd-x64": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-arm64-gnu": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-arm64-musl": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-x64-gnu": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-x64-musl": "npm:1.0.1" + "@napi-rs/wasm-tools-wasm32-wasi": "npm:1.0.1" + "@napi-rs/wasm-tools-win32-arm64-msvc": "npm:1.0.1" + "@napi-rs/wasm-tools-win32-ia32-msvc": "npm:1.0.1" + "@napi-rs/wasm-tools-win32-x64-msvc": "npm:1.0.1" + dependenciesMeta: + "@napi-rs/wasm-tools-android-arm-eabi": + optional: true + "@napi-rs/wasm-tools-android-arm64": + optional: true + "@napi-rs/wasm-tools-darwin-arm64": + optional: true + "@napi-rs/wasm-tools-darwin-x64": + optional: true + "@napi-rs/wasm-tools-freebsd-x64": + optional: true + "@napi-rs/wasm-tools-linux-arm64-gnu": + optional: true + "@napi-rs/wasm-tools-linux-arm64-musl": + optional: true + "@napi-rs/wasm-tools-linux-x64-gnu": + optional: true + "@napi-rs/wasm-tools-linux-x64-musl": + optional: true + "@napi-rs/wasm-tools-wasm32-wasi": + optional: true + "@napi-rs/wasm-tools-win32-arm64-msvc": + optional: true + "@napi-rs/wasm-tools-win32-ia32-msvc": + optional: true + "@napi-rs/wasm-tools-win32-x64-msvc": + optional: true + checksum: 10c0/bee9258e0b16a2415acc57d9aa281fa50402b38f631aea28e3a8ecd16415cdfffade63313bca777e85c3a73b80cce899ac3dd35eba9104951d7da1eb28913122 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 + languageName: node + linkType: hard + +"@octokit/auth-token@npm:^6.0.0": + version: 6.0.0 + resolution: "@octokit/auth-token@npm:6.0.0" + checksum: 10c0/32ecc904c5f6f4e5d090bfcc679d70318690c0a0b5040cd9a25811ad9dcd44c33f2cf96b6dbee1cd56cf58fde28fb1819c01b58718aa5c971f79c822357cb5c0 + languageName: node + linkType: hard + +"@octokit/core@npm:^7.0.6": + version: 7.0.6 + resolution: "@octokit/core@npm:7.0.6" + dependencies: + "@octokit/auth-token": "npm:^6.0.0" + "@octokit/graphql": "npm:^9.0.3" + "@octokit/request": "npm:^10.0.6" + "@octokit/request-error": "npm:^7.0.2" + "@octokit/types": "npm:^16.0.0" + before-after-hook: "npm:^4.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/95a328ff7c7223d9eb4aa778c63171828514ae0e0f588d33beb81a4dc03bbeae055382f6060ce23c979ab46272409942ff2cf3172109999e48429c47055b1fbe + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^11.0.2": + version: 11.0.2 + resolution: "@octokit/endpoint@npm:11.0.2" + dependencies: + "@octokit/types": "npm:^16.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/878ac12fbccff772968689b4744590677c5a3f12bebe31544832c84761bf1c6be521e8a3af07abffc9455a74dd4d1f350d714fc46fd7ce14a0a2b5f2d4e3a84c + languageName: node + linkType: hard + +"@octokit/graphql@npm:^9.0.3": + version: 9.0.3 + resolution: "@octokit/graphql@npm:9.0.3" + dependencies: + "@octokit/request": "npm:^10.0.6" + "@octokit/types": "npm:^16.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/58588d3fb2834f64244fa5376ca7922a30117b001b621e141fab0d52806370803ab0c046ac99b120fa5f45b770f52a815157fb6ffc147fc6c1da4047c1f1af49 + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^27.0.0": + version: 27.0.0 + resolution: "@octokit/openapi-types@npm:27.0.0" + checksum: 10c0/602d1de033da180a2e982cdbd3646bd5b2e16ecf36b9955a0f23e37ae9e6cb086abb48ff2ae6f2de000fce03e8ae9051794611ae4a95a8f5f6fb63276e7b8e31 + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^14.0.0": + version: 14.0.0 + resolution: "@octokit/plugin-paginate-rest@npm:14.0.0" + dependencies: + "@octokit/types": "npm:^16.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/841d79d4ccfe18fc809a4a67529b75c1dcdda13399bf4bf5b48ce7559c8b4b2cd422e3204bad4cbdea31c0cf0943521067415268e5bcfc615a3b813e058cad6b + languageName: node + linkType: hard + +"@octokit/plugin-request-log@npm:^6.0.0": + version: 6.0.0 + resolution: "@octokit/plugin-request-log@npm:6.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/40e46ad0c77235742d0bf698ab4e17df1ae06e0d7824ffc5867ed71e27de860875adb73d89629b823fe8647459a8f262c26ed1aa6ee374873fa94095f37df0bb + languageName: node + linkType: hard + +"@octokit/plugin-rest-endpoint-methods@npm:^17.0.0": + version: 17.0.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:17.0.0" + dependencies: + "@octokit/types": "npm:^16.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/cf9984d7cf6a36ff7ff1b86078ae45fe246e3df10fcef0bccf20c8cfd27bf5e7d98dcb9cf5a7b56332b9c6fa30be28d159c2987d272a4758f77056903d94402f + languageName: node + linkType: hard + +"@octokit/request-error@npm:^7.0.2": + version: 7.1.0 + resolution: "@octokit/request-error@npm:7.1.0" + dependencies: + "@octokit/types": "npm:^16.0.0" + checksum: 10c0/62b90a54545c36a30b5ffdda42e302c751be184d85b68ffc7f1242c51d7ca54dbd185b7d0027b491991776923a910c85c9c51269fe0d86111bac187507a5abc4 + languageName: node + linkType: hard + +"@octokit/request@npm:^10.0.6": + version: 10.0.7 + resolution: "@octokit/request@npm:10.0.7" + dependencies: + "@octokit/endpoint": "npm:^11.0.2" + "@octokit/request-error": "npm:^7.0.2" + "@octokit/types": "npm:^16.0.0" + fast-content-type-parse: "npm:^3.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/f789a75bf681b204ccd3d538921db662e148ed980005158d80ec4f16811e9ab73f375d4f30ef697852abd748a62f025060ea1b0c5198ec9c2e8d04e355064390 + languageName: node + linkType: hard + +"@octokit/rest@npm:^22.0.1": + version: 22.0.1 + resolution: "@octokit/rest@npm:22.0.1" + dependencies: + "@octokit/core": "npm:^7.0.6" + "@octokit/plugin-paginate-rest": "npm:^14.0.0" + "@octokit/plugin-request-log": "npm:^6.0.0" + "@octokit/plugin-rest-endpoint-methods": "npm:^17.0.0" + checksum: 10c0/f3abd84e887cc837973214ce70720a9bba53f5575f40601c6122aa25206e9055d859c0388437f0a137f6cd0e4ff405e1b46b903475b0db32a17bada0c6513d5b + languageName: node + linkType: hard + +"@octokit/types@npm:^16.0.0": + version: 16.0.0 + resolution: "@octokit/types@npm:16.0.0" + dependencies: + "@octokit/openapi-types": "npm:^27.0.0" + checksum: 10c0/b8d41098ba6fc194d13d641f9441347e3a3b96c0efabac0e14f57319340a2d4d1c8676e4cb37ab3062c5c323c617e790b0126916e9bf7b201b0cced0826f8ae2 + languageName: node + linkType: hard + +"@oras-project/oci-client@workspace:.": + version: 0.0.0-use.local + resolution: "@oras-project/oci-client@workspace:." + dependencies: + "@napi-rs/cli": "npm:^3.2.0" + "@oxc-node/core": "npm:^0.0.35" + "@types/node": "npm:^20.0.0" + ava: "npm:^6.4.1" + oxlint: "npm:^1.14.0" + typescript: "npm:^5.9.2" + languageName: unknown + linkType: soft + +"@oxc-node/core-android-arm-eabi@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-android-arm-eabi@npm:0.0.35" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@oxc-node/core-android-arm64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-android-arm64@npm:0.0.35" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-darwin-arm64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-darwin-arm64@npm:0.0.35" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-darwin-x64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-darwin-x64@npm:0.0.35" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@oxc-node/core-freebsd-x64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-freebsd-x64@npm:0.0.35" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@oxc-node/core-linux-arm-gnueabihf@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-arm-gnueabihf@npm:0.0.35" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@oxc-node/core-linux-arm64-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-arm64-gnu@npm:0.0.35" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-arm64-musl@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-arm64-musl@npm:0.0.35" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@oxc-node/core-linux-ppc64-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-ppc64-gnu@npm:0.0.35" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-s390x-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-s390x-gnu@npm:0.0.35" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-x64-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-x64-gnu@npm:0.0.35" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-x64-musl@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-x64-musl@npm:0.0.35" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@oxc-node/core-openharmony-arm64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-openharmony-arm64@npm:0.0.35" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-wasm32-wasi@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-wasm32-wasi@npm:0.0.35" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.0.7" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@oxc-node/core-win32-arm64-msvc@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-win32-arm64-msvc@npm:0.0.35" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-win32-ia32-msvc@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-win32-ia32-msvc@npm:0.0.35" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@oxc-node/core-win32-x64-msvc@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-win32-x64-msvc@npm:0.0.35" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@oxc-node/core@npm:^0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core@npm:0.0.35" + dependencies: + "@oxc-node/core-android-arm-eabi": "npm:0.0.35" + "@oxc-node/core-android-arm64": "npm:0.0.35" + "@oxc-node/core-darwin-arm64": "npm:0.0.35" + "@oxc-node/core-darwin-x64": "npm:0.0.35" + "@oxc-node/core-freebsd-x64": "npm:0.0.35" + "@oxc-node/core-linux-arm-gnueabihf": "npm:0.0.35" + "@oxc-node/core-linux-arm64-gnu": "npm:0.0.35" + "@oxc-node/core-linux-arm64-musl": "npm:0.0.35" + "@oxc-node/core-linux-ppc64-gnu": "npm:0.0.35" + "@oxc-node/core-linux-s390x-gnu": "npm:0.0.35" + "@oxc-node/core-linux-x64-gnu": "npm:0.0.35" + "@oxc-node/core-linux-x64-musl": "npm:0.0.35" + "@oxc-node/core-openharmony-arm64": "npm:0.0.35" + "@oxc-node/core-wasm32-wasi": "npm:0.0.35" + "@oxc-node/core-win32-arm64-msvc": "npm:0.0.35" + "@oxc-node/core-win32-ia32-msvc": "npm:0.0.35" + "@oxc-node/core-win32-x64-msvc": "npm:0.0.35" + pirates: "npm:^4.0.7" + dependenciesMeta: + "@oxc-node/core-android-arm-eabi": + optional: true + "@oxc-node/core-android-arm64": + optional: true + "@oxc-node/core-darwin-arm64": + optional: true + "@oxc-node/core-darwin-x64": + optional: true + "@oxc-node/core-freebsd-x64": + optional: true + "@oxc-node/core-linux-arm-gnueabihf": + optional: true + "@oxc-node/core-linux-arm64-gnu": + optional: true + "@oxc-node/core-linux-arm64-musl": + optional: true + "@oxc-node/core-linux-ppc64-gnu": + optional: true + "@oxc-node/core-linux-s390x-gnu": + optional: true + "@oxc-node/core-linux-x64-gnu": + optional: true + "@oxc-node/core-linux-x64-musl": + optional: true + "@oxc-node/core-openharmony-arm64": + optional: true + "@oxc-node/core-wasm32-wasi": + optional: true + "@oxc-node/core-win32-arm64-msvc": + optional: true + "@oxc-node/core-win32-ia32-msvc": + optional: true + "@oxc-node/core-win32-x64-msvc": + optional: true + checksum: 10c0/6328427d833c99b498f41b8dc58ed3503d17aabe66172a5aacce06c7e85377649aa6ac602dfa5dd8f8f01167b5fe428a65d751c40386eced495acbe9eb414e10 + languageName: node + linkType: hard + +"@oxlint/darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/darwin-arm64@npm:1.32.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/darwin-x64@npm:1.32.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@oxlint/linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/linux-arm64-gnu@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/linux-arm64-musl@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@oxlint/linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/linux-x64-gnu@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/linux-x64-musl@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@oxlint/win32-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/win32-arm64@npm:1.32.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/win32-x64@npm:1.32.0": + version: 1.32.0 + resolution: "@oxlint/win32-x64@npm:1.32.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^5.1.3": + version: 5.3.0 + resolution: "@rollup/pluginutils@npm:5.3.0" + dependencies: + "@types/estree": "npm:^1.0.0" + estree-walker: "npm:^2.0.2" + picomatch: "npm:^4.0.2" + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/001834bf62d7cf5bac424d2617c113f7f7d3b2bf3c1778cbcccb72cdc957b68989f8e7747c782c2b911f1dde8257f56f8ac1e779e29e74e638e3f1e2cac2bcd0 + languageName: node + linkType: hard + +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 + languageName: node + linkType: hard + +"@tybys/wasm-util@npm:^0.10.1": + version: 0.10.1 + resolution: "@tybys/wasm-util@npm:0.10.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/b255094f293794c6d2289300c5fbcafbb5532a3aed3a5ffd2f8dc1828e639b88d75f6a376dd8f94347a44813fd7a7149d8463477a9a49525c8b2dcaa38c2d1e8 + languageName: node + linkType: hard + +"@types/estree@npm:^1.0.0": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 + languageName: node + linkType: hard + +"@types/node@npm:^20.0.0": + version: 20.19.26 + resolution: "@types/node@npm:20.19.26" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/68e7d92dd2b7bddff9dffabb9c740e655906ceac428dcf070915cdcae720579e4d72261c55ed7eccbfa907a75cbb1ff3a9148ea49878a07a72d5dd6c9e06d9d7 + languageName: node + linkType: hard + +"@vercel/nft@npm:^0.29.4": + version: 0.29.4 + resolution: "@vercel/nft@npm:0.29.4" + dependencies: + "@mapbox/node-pre-gyp": "npm:^2.0.0" + "@rollup/pluginutils": "npm:^5.1.3" + acorn: "npm:^8.6.0" + acorn-import-attributes: "npm:^1.9.5" + async-sema: "npm:^3.1.1" + bindings: "npm:^1.4.0" + estree-walker: "npm:2.0.2" + glob: "npm:^10.4.5" + graceful-fs: "npm:^4.2.9" + node-gyp-build: "npm:^4.2.2" + picomatch: "npm:^4.0.2" + resolve-from: "npm:^5.0.0" + bin: + nft: out/cli.js + checksum: 10c0/84ba32c685f9d7c2c849b1e8c963d3b7eb09d122e666143ed97c3776f5b04a4745605e1d29fd81383f72b1d1c0d7d58e39f06dc92f021b5de079dfa4e8523574 + languageName: node + linkType: hard + +"abbrev@npm:^3.0.0": + version: 3.0.1 + resolution: "abbrev@npm:3.0.1" + checksum: 10c0/21ba8f574ea57a3106d6d35623f2c4a9111d9ee3e9a5be47baed46ec2457d2eac46e07a5c4a60186f88cb98abbe3e24f2d4cca70bc2b12f1692523e2209a9ccf + languageName: node + linkType: hard + +"acorn-import-attributes@npm:^1.9.5": + version: 1.9.5 + resolution: "acorn-import-attributes@npm:1.9.5" + peerDependencies: + acorn: ^8 + checksum: 10c0/5926eaaead2326d5a86f322ff1b617b0f698aa61dc719a5baa0e9d955c9885cc71febac3fb5bacff71bbf2c4f9c12db2056883c68c53eb962c048b952e1e013d + languageName: node + linkType: hard + +"acorn-walk@npm:^8.3.4": + version: 8.3.4 + resolution: "acorn-walk@npm:8.3.4" + dependencies: + acorn: "npm:^8.11.0" + checksum: 10c0/76537ac5fb2c37a64560feaf3342023dadc086c46da57da363e64c6148dc21b57d49ace26f949e225063acb6fb441eabffd89f7a3066de5ad37ab3e328927c62 + languageName: node + linkType: hard + +"acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.6.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" + bin: + acorn: bin/acorn + checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec + languageName: node + linkType: hard + +"agent-base@npm:^7.1.2": + version: 7.1.4 + resolution: "agent-base@npm:7.1.4" + checksum: 10c0/c2c9ab7599692d594b6a161559ada307b7a624fa4c7b03e3afdb5a5e31cd0e53269115b620fcab024c5ac6a6f37fa5eb2e004f076ad30f5f7e6b8b671f7b35fe + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.2.2 + resolution: "ansi-regex@npm:6.2.2" + checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 + languageName: node + linkType: hard + +"argparse@npm:^1.0.7": + version: 1.0.10 + resolution: "argparse@npm:1.0.10" + dependencies: + sprintf-js: "npm:~1.0.2" + checksum: 10c0/b2972c5c23c63df66bca144dbc65d180efa74f25f8fd9b7d9a0a6c88ae839db32df3d54770dcb6460cf840d232b60695d1a6b1053f599d84e73f7437087712de + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + +"array-find-index@npm:^1.0.1": + version: 1.0.2 + resolution: "array-find-index@npm:1.0.2" + checksum: 10c0/86b9485c74ddd324feab807e10a6de3f9c1683856267236fac4bb4d4667ada6463e106db3f6c540ae6b720e0442b590ec701d13676df4c6af30ebf4da09b4f57 + languageName: node + linkType: hard + +"arrgv@npm:^1.0.2": + version: 1.0.2 + resolution: "arrgv@npm:1.0.2" + checksum: 10c0/7e6e782e6b749923ac7cbc4048ef6fe0844c4a59bfc8932fcd4c44566ba25eed46501f94dd7cf3c7297da88f3f599ca056bfb77d0c2484aebc92f04239f69124 + languageName: node + linkType: hard + +"arrify@npm:^3.0.0": + version: 3.0.0 + resolution: "arrify@npm:3.0.0" + checksum: 10c0/2e26601b8486f29780f1f70f7ac05a226755814c2a3ab42e196748f650af1dc310cd575a11dd4b9841c70fd7460b2dd2b8fe6fb7a3375878e2660706efafa58e + languageName: node + linkType: hard + +"async-sema@npm:^3.1.1": + version: 3.1.1 + resolution: "async-sema@npm:3.1.1" + checksum: 10c0/a16da9f7f2dbdd00a969bf264b7ad331b59df3eac2b38f529b881c5cc8662594e68ed096d927ec2aabdc13454379cdc6d677bcdb0a3d2db338fb4be17957832b + languageName: node + linkType: hard + +"ava@npm:^6.4.1": + version: 6.4.1 + resolution: "ava@npm:6.4.1" + dependencies: + "@vercel/nft": "npm:^0.29.4" + acorn: "npm:^8.15.0" + acorn-walk: "npm:^8.3.4" + ansi-styles: "npm:^6.2.1" + arrgv: "npm:^1.0.2" + arrify: "npm:^3.0.0" + callsites: "npm:^4.2.0" + cbor: "npm:^10.0.9" + chalk: "npm:^5.4.1" + chunkd: "npm:^2.0.1" + ci-info: "npm:^4.3.0" + ci-parallel-vars: "npm:^1.0.1" + cli-truncate: "npm:^4.0.0" + code-excerpt: "npm:^4.0.0" + common-path-prefix: "npm:^3.0.0" + concordance: "npm:^5.0.4" + currently-unhandled: "npm:^0.4.1" + debug: "npm:^4.4.1" + emittery: "npm:^1.2.0" + figures: "npm:^6.1.0" + globby: "npm:^14.1.0" + ignore-by-default: "npm:^2.1.0" + indent-string: "npm:^5.0.0" + is-plain-object: "npm:^5.0.0" + is-promise: "npm:^4.0.0" + matcher: "npm:^5.0.0" + memoize: "npm:^10.1.0" + ms: "npm:^2.1.3" + p-map: "npm:^7.0.3" + package-config: "npm:^5.0.0" + picomatch: "npm:^4.0.2" + plur: "npm:^5.1.0" + pretty-ms: "npm:^9.2.0" + resolve-cwd: "npm:^3.0.0" + stack-utils: "npm:^2.0.6" + strip-ansi: "npm:^7.1.0" + supertap: "npm:^3.0.1" + temp-dir: "npm:^3.0.0" + write-file-atomic: "npm:^6.0.0" + yargs: "npm:^17.7.2" + peerDependencies: + "@ava/typescript": "*" + peerDependenciesMeta: + "@ava/typescript": + optional: true + bin: + ava: entrypoints/cli.mjs + checksum: 10c0/21972df1031ef46533ea1b7daa132a5fc66841c8a221b6901163d12d2a1cac39bfd8a6d3459da7eb9344fa90fc02f237f2fe2aac8785d04bf5894fa43625be28 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"before-after-hook@npm:^4.0.0": + version: 4.0.0 + resolution: "before-after-hook@npm:4.0.0" + checksum: 10c0/9f8ae8d1b06142bcfb9ef6625226b5e50348bb11210f266660eddcf9734e0db6f9afc4cb48397ee3f5ac0a3728f3ae401cdeea88413f7bed748a71db84657be2 + languageName: node + linkType: hard + +"bindings@npm:^1.4.0": + version: 1.5.0 + resolution: "bindings@npm:1.5.0" + dependencies: + file-uri-to-path: "npm:1.0.0" + checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba + languageName: node + linkType: hard + +"blueimp-md5@npm:^2.10.0": + version: 2.19.0 + resolution: "blueimp-md5@npm:2.19.0" + checksum: 10c0/85d04343537dd99a288c62450341dcce7380d3454c81f8e5a971ddd80307d6f9ef51b5b92ad7d48aaaa92fd6d3a1f6b2f4fada068faae646887f7bfabc17a346 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.2 + resolution: "brace-expansion@npm:2.0.2" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf + languageName: node + linkType: hard + +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 + languageName: node + linkType: hard + +"callsites@npm:^4.2.0": + version: 4.2.0 + resolution: "callsites@npm:4.2.0" + checksum: 10c0/8f7e269ec09fc0946bb22d838a8bc7932e1909ab4a833b964749f4d0e8bdeaa1f253287c4f911f61781f09620b6925ccd19a5ea4897489c4e59442c660c312a3 + languageName: node + linkType: hard + +"cbor@npm:^10.0.9": + version: 10.0.11 + resolution: "cbor@npm:10.0.11" + dependencies: + nofilter: "npm:^3.0.2" + checksum: 10c0/0cb6fb3d5e98c7af4443200ff107049f6132b5649b8a0e586940ca811e5ab5622bf3d0a36f154f43107acfd9685cc462e6eac77876ef4c060bcec96c71b90d8a + languageName: node + linkType: hard + +"chalk@npm:^5.4.1": + version: 5.6.2 + resolution: "chalk@npm:5.6.2" + checksum: 10c0/99a4b0f0e7991796b1e7e3f52dceb9137cae2a9dfc8fc0784a550dc4c558e15ab32ed70b14b21b52beb2679b4892b41a0aa44249bcb996f01e125d58477c6976 + languageName: node + linkType: hard + +"chardet@npm:^2.1.1": + version: 2.1.1 + resolution: "chardet@npm:2.1.1" + checksum: 10c0/d8391dd412338442b3de0d3a488aa9327f8bcf74b62b8723d6bd0b85c4084d50b731320e0a7c710edb1d44de75969995d2784b80e4c13b004a6c7a0db4c6e793 + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"chunkd@npm:^2.0.1": + version: 2.0.1 + resolution: "chunkd@npm:2.0.1" + checksum: 10c0/4e0c5aac6048ecedfa4cd0a5f6c4f010c70a7b7645aeca7bfeb47cb0733c3463054f0ced3f2667b2e0e67edd75d68a8e05481b01115ba3f8a952a93026254504 + languageName: node + linkType: hard + +"ci-info@npm:^4.3.0": + version: 4.3.1 + resolution: "ci-info@npm:4.3.1" + checksum: 10c0/7dd82000f514d76ddfe7775e4cb0d66e5c638f5fa0e2a3be29557e898da0d32ac04f231217d414d07fb968b1fbc6d980ee17ddde0d2c516f23da9cfff608f6c1 + languageName: node + linkType: hard + +"ci-parallel-vars@npm:^1.0.1": + version: 1.0.1 + resolution: "ci-parallel-vars@npm:1.0.1" + checksum: 10c0/80952f699cbbc146092b077b4f3e28d085620eb4e6be37f069b4dbb3db0ee70e8eec3beef4ebe70ff60631e9fc743b9d0869678489f167442cac08b260e5ac08 + languageName: node + linkType: hard + +"cli-truncate@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-truncate@npm:4.0.0" + dependencies: + slice-ansi: "npm:^5.0.0" + string-width: "npm:^7.0.0" + checksum: 10c0/d7f0b73e3d9b88cb496e6c086df7410b541b56a43d18ade6a573c9c18bd001b1c3fba1ad578f741a4218fdc794d042385f8ac02c25e1c295a2d8b9f3cb86eb4c + languageName: node + linkType: hard + +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: 10c0/1fbd56413578f6117abcaf858903ba1f4ad78370a4032f916745fa2c7e390183a9d9029cf837df320b0fdce8137668e522f60a30a5f3d6529ff3872d265a955f + languageName: node + linkType: hard + +"clipanion@npm:^4.0.0-rc.4": + version: 4.0.0-rc.4 + resolution: "clipanion@npm:4.0.0-rc.4" + dependencies: + typanion: "npm:^3.8.0" + peerDependencies: + typanion: "*" + checksum: 10c0/047b415b59a5e9777d00690fba563ccc850eca6bf27790a88d1deea3ecc8a89840ae9aed554ff284cc698a9f3f20256e43c25ff4a7c4c90a71e5e7d9dca61dd1 + languageName: node + linkType: hard + +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + +"code-excerpt@npm:^4.0.0": + version: 4.0.0 + resolution: "code-excerpt@npm:4.0.0" + dependencies: + convert-to-spaces: "npm:^2.0.1" + checksum: 10c0/b6c5a06e039cecd2ab6a0e10ee0831de8362107d1f298ca3558b5f9004cb8e0260b02dd6c07f57b9a0e346c76864d2873311ee1989809fdeb05bd5fbbadde773 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"colorette@npm:^2.0.20": + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40 + languageName: node + linkType: hard + +"common-path-prefix@npm:^3.0.0": + version: 3.0.0 + resolution: "common-path-prefix@npm:3.0.0" + checksum: 10c0/c4a74294e1b1570f4a8ab435285d185a03976c323caa16359053e749db4fde44e3e6586c29cd051100335e11895767cbbd27ea389108e327d62f38daf4548fdb + languageName: node + linkType: hard + +"concordance@npm:^5.0.4": + version: 5.0.4 + resolution: "concordance@npm:5.0.4" + dependencies: + date-time: "npm:^3.1.0" + esutils: "npm:^2.0.3" + fast-diff: "npm:^1.2.0" + js-string-escape: "npm:^1.0.1" + lodash: "npm:^4.17.15" + md5-hex: "npm:^3.0.1" + semver: "npm:^7.3.2" + well-known-symbols: "npm:^2.0.0" + checksum: 10c0/59b440f330df3a7c9aa148ba588b3e99aed86acab225b4f01ffcea34ace4cf11f817e31153254e8f38ed48508998dad40b9106951a743c334d751f7ab21afb8a + languageName: node + linkType: hard + +"consola@npm:^3.2.3": + version: 3.4.2 + resolution: "consola@npm:3.4.2" + checksum: 10c0/7cebe57ecf646ba74b300bcce23bff43034ed6fbec9f7e39c27cee1dc00df8a21cd336b466ad32e304ea70fba04ec9e890c200270de9a526ce021ba8a7e4c11a + languageName: node + linkType: hard + +"convert-to-spaces@npm:^2.0.1": + version: 2.0.1 + resolution: "convert-to-spaces@npm:2.0.1" + checksum: 10c0/d90aa0e3b6a27f9d5265a8d32def3c5c855b3e823a9db1f26d772f8146d6b91020a2fdfd905ce8048a73fad3aaf836fef8188c67602c374405e2ae8396c4ac46 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + +"currently-unhandled@npm:^0.4.1": + version: 0.4.1 + resolution: "currently-unhandled@npm:0.4.1" + dependencies: + array-find-index: "npm:^1.0.1" + checksum: 10c0/32d197689ec32f035910202c1abb0dc6424dce01d7b51779c685119b380d98535c110ffff67a262fc7e367612a7dfd30d3d3055f9a6634b5a9dd1302de7ef11c + languageName: node + linkType: hard + +"date-time@npm:^3.1.0": + version: 3.1.0 + resolution: "date-time@npm:3.1.0" + dependencies: + time-zone: "npm:^1.0.0" + checksum: 10c0/aa3e2e930d74b0b9e90f69de7a16d3376e30f21f1f4ce9a2311d8fec32d760e776efea752dafad0ce188187265235229013036202be053fc2d7979813bfb6ded + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"detect-libc@npm:^2.0.0": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"emittery@npm:^1.2.0": + version: 1.2.0 + resolution: "emittery@npm:1.2.0" + checksum: 10c0/3b16d67b2cbbc19d44fa124684039956dc94c376cefa8c7b29f4c934d9d370e6819f642cddaa343b83b1fc03fda554a1498e12f5861caf9d6f6394ff4b6e808a + languageName: node + linkType: hard + +"emnapi@npm:^1.7.1": + version: 1.7.1 + resolution: "emnapi@npm:1.7.1" + peerDependencies: + node-addon-api: ">= 6.1.0" + peerDependenciesMeta: + node-addon-api: + optional: true + checksum: 10c0/e3b223cb75bed94a8d11a3fa4c1b621bf32556e3c31239c589593fc275df80630af1a113fb2598436c2b08d7d8fd0099d9d24a3fc08cabddca5b0aa044a14cf2 + languageName: node + linkType: hard + +"emoji-regex@npm:^10.3.0": + version: 10.6.0 + resolution: "emoji-regex@npm:10.6.0" + checksum: 10c0/1e4aa097bb007301c3b4b1913879ae27327fdc48e93eeefefe3b87e495eb33c5af155300be951b4349ff6ac084f4403dc9eff970acba7c1c572d89396a9a32d7 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"es-toolkit@npm:^1.41.0": + version: 1.42.0 + resolution: "es-toolkit@npm:1.42.0" + dependenciesMeta: + "@trivago/prettier-plugin-sort-imports@4.3.0": + unplugged: true + prettier-plugin-sort-re-exports@0.0.1: + unplugged: true + checksum: 10c0/ee577b23336296116be423a5d01a6af827c80a10971507ae26cdb146b60ce0a930bf7bdb719ac0dc4f962ec74542a2423b934e24eb47efe1bc911862b12da109 + languageName: node + linkType: hard + +"escalade@npm:^3.1.1": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^5.0.0": + version: 5.0.0 + resolution: "escape-string-regexp@npm:5.0.0" + checksum: 10c0/6366f474c6f37a802800a435232395e04e9885919873e382b157ab7e8f0feb8fed71497f84a6f6a81a49aab41815522f5839112bd38026d203aea0c91622df95 + languageName: node + linkType: hard + +"esprima@npm:^4.0.0": + version: 4.0.1 + resolution: "esprima@npm:4.0.1" + bin: + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: 10c0/ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3 + languageName: node + linkType: hard + +"estree-walker@npm:2.0.2, estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af + languageName: node + linkType: hard + +"esutils@npm:^2.0.3": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 + languageName: node + linkType: hard + +"fast-content-type-parse@npm:^3.0.0": + version: 3.0.0 + resolution: "fast-content-type-parse@npm:3.0.0" + checksum: 10c0/06251880c83b7118af3a5e66e8bcee60d44f48b39396fc60acc2b4630bd5f3e77552b999b5c8e943d45a818854360e5e97164c374ec4b562b4df96a2cdf2e188 + languageName: node + linkType: hard + +"fast-diff@npm:^1.2.0": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: 10c0/5c19af237edb5d5effda008c891a18a585f74bf12953be57923f17a3a4d0979565fc64dbc73b9e20926b9d895f5b690c618cbb969af0cf022e3222471220ad29 + languageName: node + linkType: hard + +"fast-glob@npm:^3.3.3": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.19.1 + resolution: "fastq@npm:1.19.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10c0/ebc6e50ac7048daaeb8e64522a1ea7a26e92b3cee5cd1c7f2316cdca81ba543aa40a136b53891446ea5c3a67ec215fbaca87ad405f102dd97012f62916905630 + languageName: node + linkType: hard + +"figures@npm:^6.1.0": + version: 6.1.0 + resolution: "figures@npm:6.1.0" + dependencies: + is-unicode-supported: "npm:^2.0.0" + checksum: 10c0/9159df4264d62ef447a3931537de92f5012210cf5135c35c010df50a2169377581378149abfe1eb238bd6acbba1c0d547b1f18e0af6eee49e30363cedaffcfe4 + languageName: node + linkType: hard + +"file-uri-to-path@npm:1.0.0": + version: 1.0.0 + resolution: "file-uri-to-path@npm:1.0.0" + checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519 + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 + languageName: node + linkType: hard + +"find-up-simple@npm:^1.0.0": + version: 1.0.1 + resolution: "find-up-simple@npm:1.0.1" + checksum: 10c0/ad34de157b7db925d50ff78302fefb28e309f3bc947c93ffca0f9b0bccf9cf1a2dc57d805d5c94ec9fc60f4838f5dbdfd2a48ecd77c23015fa44c6dd5f60bc40 + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" + dependencies: + cross-spawn: "npm:^7.0.6" + signal-exit: "npm:^4.0.1" + checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + +"get-east-asian-width@npm:^1.0.0": + version: 1.4.0 + resolution: "get-east-asian-width@npm:1.4.0" + checksum: 10c0/4e481d418e5a32061c36fbb90d1b225a254cc5b2df5f0b25da215dcd335a3c111f0c2023ffda43140727a9cafb62dac41d022da82c08f31083ee89f714ee3b83 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee + languageName: node + linkType: hard + +"glob@npm:^10.4.5": + version: 10.5.0 + resolution: "glob@npm:10.5.0" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/100705eddbde6323e7b35e1d1ac28bcb58322095bd8e63a7d0bef1a2cdafe0d0f7922a981b2b48369a4f8c1b077be5c171804534c3509dfe950dde15fbe6d828 + languageName: node + linkType: hard + +"globby@npm:^14.1.0": + version: 14.1.0 + resolution: "globby@npm:14.1.0" + dependencies: + "@sindresorhus/merge-streams": "npm:^2.1.0" + fast-glob: "npm:^3.3.3" + ignore: "npm:^7.0.3" + path-type: "npm:^6.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.3.0" + checksum: 10c0/527a1063c5958255969620c6fa4444a2b2e9278caddd571d46dfbfa307cb15977afb746e84d682ba5b6c94fc081e8997f80ff05dd235441ba1cb16f86153e58e + languageName: node + linkType: hard + +"graceful-fs@npm:^4.2.9": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.5": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:4" + checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac + languageName: node + linkType: hard + +"iconv-lite@npm:^0.7.0": + version: 0.7.0 + resolution: "iconv-lite@npm:0.7.0" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/2382400469071c55b6746c531eed5fa4d033e5db6690b7331fb2a5f59a30d7a9782932e92253db26df33c1cf46fa200a3fbe524a2a7c62037c762283f188ec2f + languageName: node + linkType: hard + +"ignore-by-default@npm:^2.1.0": + version: 2.1.0 + resolution: "ignore-by-default@npm:2.1.0" + checksum: 10c0/3a6040dac25ed9da39dee73bf1634fdd1e15b0eb7cf52a6bdec81c310565782d8811c104ce40acb3d690d61c5fc38a91c78e6baee830a8a2232424dbc6b66981 + languageName: node + linkType: hard + +"ignore@npm:^7.0.3": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 + languageName: node + linkType: hard + +"indent-string@npm:^5.0.0": + version: 5.0.0 + resolution: "indent-string@npm:5.0.0" + checksum: 10c0/8ee77b57d92e71745e133f6f444d6fa3ed503ad0e1bcd7e80c8da08b42375c07117128d670589725ed07b1978065803fa86318c309ba45415b7fe13e7f170220 + languageName: node + linkType: hard + +"irregular-plurals@npm:^3.3.0": + version: 3.5.0 + resolution: "irregular-plurals@npm:3.5.0" + checksum: 10c0/7c033bbe7325e5a6e0a26949cc6863b6ce273403d4cd5b93bd99b33fecb6605b0884097c4259c23ed0c52c2133bf7d1cdcdd7a0630e8c325161fe269b3447918 + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^4.0.0": + version: 4.0.0 + resolution: "is-fullwidth-code-point@npm:4.0.0" + checksum: 10c0/df2a717e813567db0f659c306d61f2f804d480752526886954a2a3e2246c7745fd07a52b5fecf2b68caf0a6c79dcdace6166fdf29cc76ed9975cc334f0a018b8 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"is-plain-object@npm:^5.0.0": + version: 5.0.0 + resolution: "is-plain-object@npm:5.0.0" + checksum: 10c0/893e42bad832aae3511c71fd61c0bf61aa3a6d853061c62a307261842727d0d25f761ce9379f7ba7226d6179db2a3157efa918e7fe26360f3bf0842d9f28942c + languageName: node + linkType: hard + +"is-promise@npm:^4.0.0": + version: 4.0.0 + resolution: "is-promise@npm:4.0.0" + checksum: 10c0/ebd5c672d73db781ab33ccb155fb9969d6028e37414d609b115cc534654c91ccd061821d5b987eefaa97cf4c62f0b909bb2f04db88306de26e91bfe8ddc01503 + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + +"js-string-escape@npm:^1.0.1": + version: 1.0.1 + resolution: "js-string-escape@npm:1.0.1" + checksum: 10c0/2c33b9ff1ba6b84681c51ca0997e7d5a1639813c95d5b61cb7ad47e55cc28fa4a0b1935c3d218710d8e6bcee5d0cd8c44755231e3a4e45fc604534d9595a3628 + languageName: node + linkType: hard + +"js-yaml@npm:^3.14.1": + version: 3.14.2 + resolution: "js-yaml@npm:3.14.2" + dependencies: + argparse: "npm:^1.0.7" + esprima: "npm:^4.0.0" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/3261f25912f5dd76605e5993d0a126c2b6c346311885d3c483706cd722efe34f697ea0331f654ce27c00a42b426e524518ec89d65ed02ea47df8ad26dcc8ce69 + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.0": + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 + languageName: node + linkType: hard + +"load-json-file@npm:^7.0.1": + version: 7.0.1 + resolution: "load-json-file@npm:7.0.1" + checksum: 10c0/7117459608a0b6329c7f78e6e1f541b3162dd901c29dd5af721fec8b270177d2e3d7999c971f344fff04daac368d052732e2c7146014bc84d15e0b636975e19a + languageName: node + linkType: hard + +"lodash@npm:^4.17.15": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c + languageName: node + linkType: hard + +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + +"matcher@npm:^5.0.0": + version: 5.0.0 + resolution: "matcher@npm:5.0.0" + dependencies: + escape-string-regexp: "npm:^5.0.0" + checksum: 10c0/eda5471fc9d5b7264d63c81727824adc3585ddb5cfdc5fce5a9b7c86f946ff181610735d330b1c37a84811df872d1290bf4e9401d2be2a414204343701144b18 + languageName: node + linkType: hard + +"md5-hex@npm:^3.0.1": + version: 3.0.1 + resolution: "md5-hex@npm:3.0.1" + dependencies: + blueimp-md5: "npm:^2.10.0" + checksum: 10c0/ee2b4d8da16b527b3a3fe4d7a96720f43afd07b46a82d49421208b5a126235fb75cfb30b80d4029514772c8844273f940bddfbf4155c787f968f3be4060d01e4 + languageName: node + linkType: hard + +"memoize@npm:^10.1.0": + version: 10.2.0 + resolution: "memoize@npm:10.2.0" + dependencies: + mimic-function: "npm:^5.0.1" + checksum: 10c0/8a5891f313e8db82bab4bb9694752aab40c11ce3bf9b8cc32228ebaf87e14e8109a23b37ceef32baadbedc09af2c4dd0d361a93e9e591bb0ed9e23f728e1ce9f + languageName: node + linkType: hard + +"merge2@npm:^1.3.0": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 + languageName: node + linkType: hard + +"mimic-function@npm:^5.0.1": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d + languageName: node + linkType: hard + +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"mute-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "mute-stream@npm:3.0.0" + checksum: 10c0/12cdb36a101694c7a6b296632e6d93a30b74401873cf7507c88861441a090c71c77a58f213acadad03bc0c8fa186639dec99d68a14497773a8744320c136e701 + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.7": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.2.2": + version: 4.8.4 + resolution: "node-gyp-build@npm:4.8.4" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 10c0/444e189907ece2081fe60e75368784f7782cfddb554b60123743dfb89509df89f1f29c03bbfa16b3a3e0be3f48799a4783f487da6203245fa5bed239ba7407e1 + languageName: node + linkType: hard + +"nofilter@npm:^3.0.2": + version: 3.1.0 + resolution: "nofilter@npm:3.1.0" + checksum: 10c0/92459f3864a067b347032263f0b536223cbfc98153913b5dce350cb39c8470bc1813366e41993f22c33cc6400c0f392aa324a4b51e24c22040635c1cdb046499 + languageName: node + linkType: hard + +"nopt@npm:^8.0.0": + version: 8.1.0 + resolution: "nopt@npm:8.1.0" + dependencies: + abbrev: "npm:^3.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/62e9ea70c7a3eb91d162d2c706b6606c041e4e7b547cbbb48f8b3695af457dd6479904d7ace600856bf923dd8d1ed0696f06195c8c20f02ac87c1da0e1d315ef + languageName: node + linkType: hard + +"obug@npm:^2.0.0": + version: 2.1.1 + resolution: "obug@npm:2.1.1" + checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78 + languageName: node + linkType: hard + +"oxlint@npm:^1.14.0": + version: 1.32.0 + resolution: "oxlint@npm:1.32.0" + dependencies: + "@oxlint/darwin-arm64": "npm:1.32.0" + "@oxlint/darwin-x64": "npm:1.32.0" + "@oxlint/linux-arm64-gnu": "npm:1.32.0" + "@oxlint/linux-arm64-musl": "npm:1.32.0" + "@oxlint/linux-x64-gnu": "npm:1.32.0" + "@oxlint/linux-x64-musl": "npm:1.32.0" + "@oxlint/win32-arm64": "npm:1.32.0" + "@oxlint/win32-x64": "npm:1.32.0" + peerDependencies: + oxlint-tsgolint: ">=0.8.1" + dependenciesMeta: + "@oxlint/darwin-arm64": + optional: true + "@oxlint/darwin-x64": + optional: true + "@oxlint/linux-arm64-gnu": + optional: true + "@oxlint/linux-arm64-musl": + optional: true + "@oxlint/linux-x64-gnu": + optional: true + "@oxlint/linux-x64-musl": + optional: true + "@oxlint/win32-arm64": + optional: true + "@oxlint/win32-x64": + optional: true + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + bin: + oxc_language_server: bin/oxc_language_server + oxlint: bin/oxlint + checksum: 10c0/a87503f30b7f44d013f147c8c492c1b71f47045e7f45e03e5d3f042f55568b14a8856f944e393a9fdd0cf380e9544192e7e586fd8f52079143ff12ac5a628c4b + languageName: node + linkType: hard + +"p-map@npm:^7.0.3": + version: 7.0.4 + resolution: "p-map@npm:7.0.4" + checksum: 10c0/a5030935d3cb2919d7e89454d1ce82141e6f9955413658b8c9403cfe379283770ed3048146b44cde168aa9e8c716505f196d5689db0ae3ce9a71521a2fef3abd + languageName: node + linkType: hard + +"package-config@npm:^5.0.0": + version: 5.0.0 + resolution: "package-config@npm:5.0.0" + dependencies: + find-up-simple: "npm:^1.0.0" + load-json-file: "npm:^7.0.1" + checksum: 10c0/f6c48930700b73a41d839bf2898b628d23665827488a4f34aed2d05e4a99d7a70a70ada115c3546765947fbc8accff94c0779da21ea084b25df47cb774531eeb + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + +"parse-ms@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-ms@npm:4.0.0" + checksum: 10c0/a7900f4f1ebac24cbf5e9708c16fb2fd482517fad353aecd7aefb8c2ba2f85ce017913ccb8925d231770404780df46244ea6fec598b3bde6490882358b4d2d16 + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + +"path-type@npm:^6.0.0": + version: 6.0.0 + resolution: "path-type@npm:6.0.0" + checksum: 10c0/55baa8b1187d6dc683d5a9cfcc866168d6adff58e5db91126795376d818eee46391e00b2a4d53e44d844c7524a7d96aa68cc68f4f3e500d3d069a39e6535481c + languageName: node + linkType: hard + +"picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be + languageName: node + linkType: hard + +"picomatch@npm:^4.0.2": + version: 4.0.3 + resolution: "picomatch@npm:4.0.3" + checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 + languageName: node + linkType: hard + +"pirates@npm:^4.0.7": + version: 4.0.7 + resolution: "pirates@npm:4.0.7" + checksum: 10c0/a51f108dd811beb779d58a76864bbd49e239fa40c7984cd11596c75a121a8cc789f1c8971d8bb15f0dbf9d48b76c05bb62fcbce840f89b688c0fa64b37e8478a + languageName: node + linkType: hard + +"plur@npm:^5.1.0": + version: 5.1.0 + resolution: "plur@npm:5.1.0" + dependencies: + irregular-plurals: "npm:^3.3.0" + checksum: 10c0/26bb622b8545fcfd47bbf56fbcca66c08693708a232e403fa3589e00003c56c14231ac57c7588ca5db83ef4be1f61383402c4ea954000768f779f8aef6eb6da8 + languageName: node + linkType: hard + +"pretty-ms@npm:^9.2.0": + version: 9.3.0 + resolution: "pretty-ms@npm:9.3.0" + dependencies: + parse-ms: "npm:^4.0.0" + checksum: 10c0/555ea39a1de48a30601938aedb76d682871d33b6dee015281c37108921514b11e1792928b1648c2e5589acc73c8ef0fb5e585fb4c718e340a28b86799e90fb34 + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + +"resolve-cwd@npm:^3.0.0": + version: 3.0.0 + resolution: "resolve-cwd@npm:3.0.0" + dependencies: + resolve-from: "npm:^5.0.0" + checksum: 10c0/e608a3ebd15356264653c32d7ecbc8fd702f94c6703ea4ac2fb81d9c359180cba0ae2e6b71faa446631ed6145454d5a56b227efc33a2d40638ac13f8beb20ee4 + languageName: node + linkType: hard + +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.1.0 + resolution: "reusify@npm:1.1.0" + checksum: 10c0/4eff0d4a5f9383566c7d7ec437b671cc51b25963bd61bf127c3f3d3f68e44a026d99b8d2f1ad344afff8d278a8fe70a8ea092650a716d22287e8bef7126bb2fa + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + +"semver@npm:^7.3.2, semver@npm:^7.5.3, semver@npm:^7.7.3": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: 10c0/4afe5c986567db82f44c8c6faef8fe9df2a9b1d98098fc1721f57c696c4c21cebd572f297fc21002f81889492345b8470473bc6f4aff5fb032a6ea59ea2bc45e + languageName: node + linkType: hard + +"serialize-error@npm:^7.0.1": + version: 7.0.1 + resolution: "serialize-error@npm:7.0.1" + dependencies: + type-fest: "npm:^0.13.1" + checksum: 10c0/7982937d578cd901276c8ab3e2c6ed8a4c174137730f1fb0402d005af209a0e84d04acc874e317c936724c7b5b26c7a96ff7e4b8d11a469f4924a4b0ea814c05 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"slash@npm:^5.1.0": + version: 5.1.0 + resolution: "slash@npm:5.1.0" + checksum: 10c0/eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3 + languageName: node + linkType: hard + +"slice-ansi@npm:^5.0.0": + version: 5.0.0 + resolution: "slice-ansi@npm:5.0.0" + dependencies: + ansi-styles: "npm:^6.0.0" + is-fullwidth-code-point: "npm:^4.0.0" + checksum: 10c0/2d4d40b2a9d5cf4e8caae3f698fe24ae31a4d778701724f578e984dcb485ec8c49f0c04dab59c401821e80fcdfe89cace9c66693b0244e40ec485d72e543914f + languageName: node + linkType: hard + +"sprintf-js@npm:~1.0.2": + version: 1.0.3 + resolution: "sprintf-js@npm:1.0.3" + checksum: 10c0/ecadcfe4c771890140da5023d43e190b7566d9cf8b2d238600f31bec0fc653f328da4450eb04bd59a431771a8e9cc0e118f0aa3974b683a4981b4e07abc2a5bb + languageName: node + linkType: hard + +"stack-utils@npm:^2.0.6": + version: 2.0.6 + resolution: "stack-utils@npm:2.0.6" + dependencies: + escape-string-regexp: "npm:^2.0.0" + checksum: 10c0/651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string-width@npm:^7.0.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": + version: 7.1.2 + resolution: "strip-ansi@npm:7.1.2" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10c0/0d6d7a023de33368fd042aab0bf48f4f4077abdfd60e5393e73c7c411e85e1b3a83507c11af2e656188511475776215df9ca589b4da2295c9455cc399ce1858b + languageName: node + linkType: hard + +"supertap@npm:^3.0.1": + version: 3.0.1 + resolution: "supertap@npm:3.0.1" + dependencies: + indent-string: "npm:^5.0.0" + js-yaml: "npm:^3.14.1" + serialize-error: "npm:^7.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/8164674f2e280cab875f0fef5bb36c15553c13e29697ff92f4e0d6bc62149f0303a89eee47535413ed145ea72e14a24d065bab233059d48a499ec5ebb4566b0f + languageName: node + linkType: hard + +"tar@npm:^7.4.0": + version: 7.5.2 + resolution: "tar@npm:7.5.2" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/a7d8b801139b52f93a7e34830db0de54c5aa45487c7cb551f6f3d44a112c67f1cb8ffdae856b05fd4f17b1749911f1c26f1e3a23bbe0279e17fd96077f13f467 + languageName: node + linkType: hard + +"temp-dir@npm:^3.0.0": + version: 3.0.0 + resolution: "temp-dir@npm:3.0.0" + checksum: 10c0/a86978a400984cd5f315b77ebf3fe53bb58c61f192278cafcb1f3fb32d584a21dc8e08b93171d7874b7cc972234d3455c467306cc1bfc4524b622e5ad3bfd671 + languageName: node + linkType: hard + +"time-zone@npm:^1.0.0": + version: 1.0.0 + resolution: "time-zone@npm:1.0.0" + checksum: 10c0/d00ebd885039109011b6e2423ebbf225160927333c2ade6d833e9cc4676db20759f1f3855fafde00d1bd668c243a6aa68938ce71fe58aab0d514e820d59c1d81 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 + languageName: node + linkType: hard + +"tslib@npm:^2.4.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"typanion@npm:^3.14.0, typanion@npm:^3.8.0": + version: 3.14.0 + resolution: "typanion@npm:3.14.0" + checksum: 10c0/8b03b19844e6955bfd906c31dc781bae6d7f1fb3ce4fe24b7501557013d4889ae5cefe671dafe98d87ead0adceb8afcb8bc16df7dc0bd2b7331bac96f3a7cae2 + languageName: node + linkType: hard + +"type-fest@npm:^0.13.1": + version: 0.13.1 + resolution: "type-fest@npm:0.13.1" + checksum: 10c0/0c0fa07ae53d4e776cf4dac30d25ad799443e9eef9226f9fddbb69242db86b08584084a99885cfa5a9dfe4c063ebdc9aa7b69da348e735baede8d43f1aeae93b + languageName: node + linkType: hard + +"typescript@npm:^5.9.2": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.9.2#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 + languageName: node + linkType: hard + +"universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2": + version: 7.0.3 + resolution: "universal-user-agent@npm:7.0.3" + checksum: 10c0/6043be466a9bb96c0ce82392842d9fddf4c37e296f7bacc2cb25f47123990eb436c82df824644f9c5070a94dbdb117be17f66d54599ab143648ec57ef93dbcc8 + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db + languageName: node + linkType: hard + +"well-known-symbols@npm:^2.0.0": + version: 2.0.0 + resolution: "well-known-symbols@npm:2.0.0" + checksum: 10c0/cb6c12e98877e8952ec28d13ae6f4fdb54ae1cb49b16a728720276dadd76c930e6cb0e174af3a4620054dd2752546f842540122920c6e31410208abd4958ee6b + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"wrap-ansi@npm:^9.0.2": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/3305839b9a0d6fb930cb63a52f34d3936013d8b0682ff3ec133c9826512620f213800ffa19ea22904876d5b7e9a3c1f40682f03597d986a4ca881fa7b033688c + languageName: node + linkType: hard + +"write-file-atomic@npm:^6.0.0": + version: 6.0.0 + resolution: "write-file-atomic@npm:6.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^4.0.1" + checksum: 10c0/ae2f1c27474758a9aca92037df6c1dd9cb94c4e4983451210bd686bfe341f142662f6aa5913095e572ab037df66b1bfe661ed4ce4c0369ed0e8219e28e141786 + languageName: node + linkType: hard + +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard + +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + languageName: node + linkType: hard